@saltcorn/markup 1.4.1 → 1.4.2

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.
@@ -0,0 +1,695 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const globals_1 = require("@jest/globals");
40
+ const mjml_layout_1 = require("./mjml-layout");
41
+ const tags = __importStar(require("./tags"));
42
+ const mjml = __importStar(require("./mjml-tags"));
43
+ const tabs_1 = __importDefault(require("./tabs"));
44
+ const table_1 = __importDefault(require("./table"));
45
+ // import index = require("./index");
46
+ const index_1 = __importDefault(require("./index"));
47
+ const builder_1 = __importDefault(require("./builder"));
48
+ const { post_btn, post_delete_btn, post_dropdown_item, settingsDropdown, localeDate, } = index_1.default;
49
+ const { a, input, div, ul, text, text_attr, i, hr, genericElement } = tags;
50
+ (0, globals_1.describe)("tags", () => {
51
+ (0, globals_1.it)("renders", () => {
52
+ (0, globals_1.expect)(a({ href: "/" }, "Home")).toBe('<a href="/">Home</a>');
53
+ (0, globals_1.expect)(a({ href: "/" }, ["Home", " Sweet", " Home"])).toBe('<a href="/">Home Sweet Home</a>');
54
+ (0, globals_1.expect)(a({ href: "/", class: "centre" }, "Home")).toBe('<a href="/" class="centre">Home</a>');
55
+ (0, globals_1.expect)(input({ type: "text" })).toBe('<input type="text">');
56
+ (0, globals_1.expect)(div(5)).toBe("<div>5</div>");
57
+ (0, globals_1.expect)(div()).toBe("<div></div>");
58
+ (0, globals_1.expect)(div(null)).toBe("<div></div>");
59
+ (0, globals_1.expect)(div("Hello world")).toBe("<div>Hello world</div>");
60
+ (0, globals_1.expect)(div(["Hello world"])).toBe("<div>Hello world</div>");
61
+ (0, globals_1.expect)(i()).toBe("<i></i>");
62
+ (0, globals_1.expect)(hr()).toBe("<hr>");
63
+ (0, globals_1.expect)(div(["hello ", "world"])).toBe("<div>hello world</div>");
64
+ (0, globals_1.expect)(ul({ class: "foo" }, [false, "hello ", "world"])).toBe(`<ul class="foo">hello world</ul>`);
65
+ (0, globals_1.expect)(ul({ class: "foo" }, [["hello ", "world"]])).toBe(`<ul class="foo">hello world</ul>`);
66
+ (0, globals_1.expect)(i({ class: "fas fa-plus-square" })).toBe('<i class="fas fa-plus-square"></i>');
67
+ (0, globals_1.expect)(genericElement("div", { class: "foo" }, "Hello")).toBe('<div class="foo">Hello</div>');
68
+ (0, globals_1.expect)(genericElement("div", { onclick: `$("#bar").hide()` }, "Hello")).toBe('<div onclick="$(&quot;#bar&quot;).hide()">Hello</div>');
69
+ });
70
+ (0, globals_1.it)("class", () => {
71
+ (0, globals_1.expect)(div({ class: "foo" }, 5)).toBe('<div class="foo">5</div>');
72
+ (0, globals_1.expect)(div({ class: false }, 5)).toBe("<div>5</div>");
73
+ (0, globals_1.expect)(div({ class: "foo bar" }, 5)).toBe('<div class="foo bar">5</div>');
74
+ (0, globals_1.expect)(div({ class: ["foo", "bar"] }, 5)).toBe('<div class="foo bar">5</div>');
75
+ (0, globals_1.expect)(div({ class: ["foo", " "] }, 5)).toBe('<div class="foo ">5</div>');
76
+ (0, globals_1.expect)(input({ class: ["foo", " "] })).toBe('<input class="foo ">');
77
+ (0, globals_1.expect)(div({ class: ["foo bar", "", undefined, null, false, "baz"] }, 5)).toBe('<div class="foo bar baz">5</div>');
78
+ (0, globals_1.expect)(div({ class: [undefined, null, false] }, 5)).toBe("<div>5</div>");
79
+ (0, globals_1.expect)(hr({ class: "foo" })).toBe('<hr class="foo">');
80
+ });
81
+ (0, globals_1.it)("style", () => {
82
+ (0, globals_1.expect)(div({ style: "color:red;border:1px solid black" }, 5)).toBe('<div style="color:red;border:1px solid black">5</div>');
83
+ (0, globals_1.expect)(div({ style: ["color:red", "border:1px solid black"] }, 5)).toBe('<div style="color:red;border:1px solid black">5</div>');
84
+ (0, globals_1.expect)(div({ style: ["color:red", false, undefined, "border:1px solid black"] }, 5)).toBe('<div style="color:red;border:1px solid black">5</div>');
85
+ (0, globals_1.expect)(div({ style: { color: "red", border: "1px solid black" } }, 5)).toBe('<div style="color:red;border:1px solid black">5</div>');
86
+ (0, globals_1.expect)(div({ style: { marginRight: "1px", border: "1px solid black" } }, 5)).toBe('<div style="margin-right:1px;border:1px solid black">5</div>');
87
+ //border-top-left-radius
88
+ (0, globals_1.expect)(div({ style: { marginRight: "1px", borderTopLeftRadius: "3px" } }, 5)).toBe('<div style="margin-right:1px;border-top-left-radius:3px">5</div>');
89
+ (0, globals_1.expect)(hr({ style: { color: "red", display: null } }, 5)).toBe('<hr style="color:red">');
90
+ (0, globals_1.expect)(hr({ style: { color: "red", display: "" } }, 5)).toBe('<hr style="color:red">');
91
+ (0, globals_1.expect)(hr({ style: { color: "red", display: "none" } }, 5)).toBe('<hr style="color:red;display:none">');
92
+ (0, globals_1.expect)(hr({ style: { color: "", display: null } }, 5)).toBe("<hr>");
93
+ (0, globals_1.expect)(hr({ style: { color: "red" } }, 5)).toBe('<hr style="color:red">');
94
+ (0, globals_1.expect)(hr({ style: {} })).toBe("<hr>");
95
+ (0, globals_1.expect)(hr({ style: null })).toBe("<hr>");
96
+ (0, globals_1.expect)(div({ class: "foo", style: null })).toBe('<div class="foo"></div>');
97
+ });
98
+ (0, globals_1.it)("escaping", () => {
99
+ (0, globals_1.expect)(text("foo")).toBe("foo");
100
+ (0, globals_1.expect)(text_attr('" onMouseOver="alert(1);')).toBe("&quot; onMouseOver=&quot;alert(1);");
101
+ (0, globals_1.expect)(text(1)).toBe("1");
102
+ (0, globals_1.expect)(text(0)).toBe("0");
103
+ (0, globals_1.expect)(text("<script>alert(1);<script>")).toBe("&lt;script&gt;alert(1);&lt;script&gt;");
104
+ (0, globals_1.expect)(text("<p>alert<p>")).toBe("<p>alert<p>");
105
+ (0, globals_1.expect)(text("<kbd>ctrl<kbd>")).toBe("<kbd>ctrl<kbd>");
106
+ (0, globals_1.expect)(text('<span style="color:#2ecc71;">green</span>')).toBe('<span style="color:#2ecc71;">green</span>');
107
+ (0, globals_1.expect)(text('<article style="color:#2ecc71;">green</article>')).toBe("<article>green</article>");
108
+ (0, globals_1.expect)(text('<article style="color:#2ecc71;">green</article>', {
109
+ article: ["style"],
110
+ })).toBe('<article style="color:#2ecc71;">green</article>');
111
+ (0, globals_1.expect)(text('<progress onclick="foo()">green</progress>', {
112
+ progress: ["onclick"],
113
+ })).toBe('<progress onclick="foo()">green</progress>');
114
+ });
115
+ });
116
+ (0, globals_1.describe)("mjml tags", () => {
117
+ (0, globals_1.it)("renders", () => {
118
+ (0, globals_1.expect)(mjml.mjml(mjml.body(mjml.section({ "background-color": "#f0f0f0" }, mjml.text("hello world"))))).toBe('<mjml><mj-body><mj-section background-color="#f0f0f0"><mj-text>hello world</mj-text></mj-section></mj-body></mjml>');
119
+ });
120
+ (0, globals_1.it)("renders emailButton with default style", () => {
121
+ const result = mjml.emailButton({
122
+ href: "https://example.com",
123
+ title: "Click Me",
124
+ }, "Click Me");
125
+ const expected = `
126
+ <a rel="noopener" target="_blank" title="Click Me" href="https://example.com" style="background-color: #6c757d; color: #ffffff; font-size: 18px; font-family: Helvetica, Arial, sans-serif; font-weight: bold; text-decoration: none; padding: 14px 20px; border-radius: 5px; display: inline-block; mso-padding-alt: 0;">
127
+ <!--[if mso]>
128
+ <i style="letter-spacing: 25px; mso-font-width: -100%; mso-text-raise: 30pt;">&nbsp;</i>
129
+ <![endif]-->
130
+ <span style="mso-text-raise: 15pt;">Click Me</span>
131
+ <!--[if mso]>
132
+ <i style="letter-spacing: 25px; mso-font-width: -100%;">&nbsp;</i>
133
+ <![endif]-->
134
+ </a>
135
+ `
136
+ .replace(/\s+/g, " ")
137
+ .trim();
138
+ (0, globals_1.expect)(result.replace(/\s+/g, " ").trim()).toBe(expected);
139
+ });
140
+ (0, globals_1.it)("renders emailButton with btn-primary style", () => {
141
+ const result = mjml.emailButton({
142
+ href: "https://example.com",
143
+ title: "Click Me",
144
+ btnStyle: "btn-primary",
145
+ }, "Click Me");
146
+ const expected = '<a rel="noopener" target="_blank" title="Click Me" href="https://example.com" style="background-color: #0d6efd; color: #ffffff; font-size: 18px; font-family: Helvetica, Arial, sans-serif; font-weight: bold; text-decoration: none; padding: 14px 20px; border-radius: 5px; display: inline-block; mso-padding-alt: 0;"> <!--[if mso]> <i style="letter-spacing: 25px; mso-font-width: -100%; mso-text-raise: 30pt;">&nbsp;</i> <![endif]--> <span style="mso-text-raise: 15pt;">Click Me</span> <!--[if mso]> <i style="letter-spacing: 25px; mso-font-width: -100%;">&nbsp;</i> <![endif]--> </a>'
147
+ .replace(/\s+/g, " ")
148
+ .trim();
149
+ (0, globals_1.expect)(result.replace(/\s+/g, " ").trim()).toBe(expected);
150
+ });
151
+ (0, globals_1.it)("renders emailButton with btn-outline-primary style", () => {
152
+ const result = mjml.emailButton({ href: "https://example.com", btnStyle: "btn-outline-primary" }, "Click Me");
153
+ const expected = '<a rel="noopener" target="_blank" href="https://example.com" style="background-color: transparent; color: #0d6efd; border-color: #0d6efd;border-width:1px; border-style: solid; font-size: 18px; font-family: Helvetica, Arial, sans-serif; font-weight: bold; text-decoration: none; padding: 14px 20px; border-radius: 5px; display: inline-block; mso-padding-alt: 0;"> <!--[if mso]> <i style="letter-spacing: 25px; mso-font-width: -100%; mso-text-raise: 30pt;">&nbsp;</i> <![endif]--> <span style="mso-text-raise: 15pt;">Click Me</span> <!--[if mso]> <i style="letter-spacing: 25px; mso-font-width: -100%;">&nbsp;</i> <![endif]--> </a>'
154
+ .replace(/\s+/g, " ")
155
+ .trim();
156
+ (0, globals_1.expect)(result.replace(/\s+/g, " ").trim()).toBe(expected);
157
+ });
158
+ (0, globals_1.it)("renders emailButton with custom title and style", () => {
159
+ const result = mjml.emailButton({
160
+ href: "https://example.com",
161
+ title: "Custom Title",
162
+ btnStyle: "btn-danger",
163
+ style: "color: #fff; background-color: #000",
164
+ }, "Click Me");
165
+ const expected = '<a rel="noopener" target="_blank" title="Custom Title" href="https://example.com" style="color: #fff; background-color: #000; border-width: 1px; border-style: solid; font-size: 18px; font-family: Helvetica, Arial, sans-serif; font-weight: bold; text-decoration: none; padding: 14px 20px; border-radius: 5px; display: inline-block; mso-padding-alt: 0;"> <!--[if mso]> <i style="letter-spacing: 25px; mso-font-width: -100%; mso-text-raise: 30pt;">&nbsp;</i> <![endif]--> <span style="mso-text-raise: 15pt;">Click Me</span> <!--[if mso]> <i style="letter-spacing: 25px; mso-font-width: -100%;">&nbsp;</i> <![endif]--> </a>'
166
+ .replace(/\s+/g, " ")
167
+ .trim();
168
+ (0, globals_1.expect)(result.replace(/\s+/g, " ").trim()).toBe(expected);
169
+ });
170
+ });
171
+ (0, globals_1.describe)("tabs", () => {
172
+ (0, globals_1.it)("renders tabs with single content", () => {
173
+ const result = (0, tabs_1.default)({
174
+ Tab1: "Content1",
175
+ });
176
+ (0, globals_1.expect)(result).toBe(`<ul class="nav nav-tabs" role="tablist"><li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#Tab1" id="Tab1-tab" role="tab" aria-controls="home" aria-selected="true">Tab1</a></li></ul><div class="tab-content"><div class="tab-pane fade show active" id="Tab1" role="tabpanel" aria-labelledby="Tab1-tab">Content1</div></div>`);
177
+ });
178
+ (0, globals_1.it)("renders tabs with multiple contents", () => {
179
+ const result = (0, tabs_1.default)({ Tab1: "Content1", Tab2: "Content2" });
180
+ (0, globals_1.expect)(result).toBe(`<ul class="nav nav-tabs" role="tablist"><li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#Tab1" id="Tab1-tab" role="tab" aria-controls="home" aria-selected="true">Tab1</a></li><li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#Tab2" id="Tab2-tab" role="tab" aria-controls="home" aria-selected="true">Tab2</a></li></ul><div class="tab-content"><div class="tab-pane fade show active" id="Tab1" role="tabpanel" aria-labelledby="Tab1-tab">Content1</div><div class="tab-pane fade" id="Tab2" role="tabpanel" aria-labelledby="Tab2-tab">Content2</div></div>`);
181
+ });
182
+ (0, globals_1.it)("renders tabs from an array of entries", () => {
183
+ const result = (0, tabs_1.default)([
184
+ ["Tab1", "Content1"],
185
+ ["Tab2", "Content2"],
186
+ ]);
187
+ (0, globals_1.expect)(result).toBe(`<ul class="nav nav-tabs" role="tablist"><li class="nav-item"><a class="nav-link active" data-bs-toggle="tab" href="#Tab1" id="Tab1-tab" role="tab" aria-controls="home" aria-selected="true">Tab1</a></li><li class="nav-item"><a class="nav-link" data-bs-toggle="tab" href="#Tab2" id="Tab2-tab" role="tab" aria-controls="home" aria-selected="true">Tab2</a></li></ul><div class="tab-content"><div class="tab-pane fade show active" id="Tab1" role="tabpanel" aria-labelledby="Tab1-tab">Content1</div><div class="tab-pane fade" id="Tab2" role="tabpanel" aria-labelledby="Tab2-tab">Content2</div></div>`);
188
+ });
189
+ (0, globals_1.it)("handles empty tabs gracefully", () => {
190
+ const result = (0, tabs_1.default)({});
191
+ (0, globals_1.expect)(result).toBe(`<ul class="nav nav-tabs" role="tablist"></ul><div class="tab-content"></div>`);
192
+ });
193
+ });
194
+ (0, globals_1.describe)("table", () => {
195
+ (0, globals_1.it)("renders a basic table", () => {
196
+ const headers = [
197
+ { label: "Name", key: "name" },
198
+ { label: "Age", key: "age" },
199
+ ];
200
+ const rows = [
201
+ { name: "UserName1", age: 25, id: 1 },
202
+ { name: "UserName2", age: 30, id: 2 },
203
+ ];
204
+ const result = (0, table_1.default)(headers, rows);
205
+ (0, globals_1.expect)(result).toBe(`<div class="table-responsive"><table class="table table-sm"><thead><tr><th>Name</th><th>Age</th></tr></thead><tbody><tr data-row-id="1"><td>UserName1</td><td>25</td></tr><tr data-row-id="2"><td>UserName2</td><td>30</td></tr></tbody></table></div>`);
206
+ });
207
+ (0, globals_1.it)("renders a tramnsposed table", () => {
208
+ const headers = [
209
+ {
210
+ label: "Name",
211
+ key: "name",
212
+ },
213
+ {
214
+ label: "Age",
215
+ key: "age",
216
+ },
217
+ ];
218
+ const rows = [
219
+ { name: "UserName1", age: 25 },
220
+ { name: "UserName2", age: 30 },
221
+ ];
222
+ const result = (0, table_1.default)(headers, rows, { transpose: true });
223
+ (0, globals_1.expect)(result).toBe(`<div class="table-responsive"><table class="table table-sm"><tbody><tr row-key="name"><th>Name</th><td>UserName1</td><td>UserName2</td></tr><tr row-key="age"><th>Age</th><td>25</td><td>30</td></tr></tbody></table></div>`);
224
+ });
225
+ (0, globals_1.it)("renders a transposed table", () => {
226
+ const headers = [
227
+ {
228
+ label: "Name",
229
+ key: "name",
230
+ },
231
+ {
232
+ label: "Age",
233
+ key: "age",
234
+ },
235
+ ];
236
+ const rows = [
237
+ { name: "UserName1", age: 25 },
238
+ { name: "UserName2", age: 30 },
239
+ ];
240
+ const result = (0, table_1.default)(headers, rows, { transpose: true });
241
+ (0, globals_1.expect)(result).toBe(`<div class="table-responsive"><table class="table table-sm"><tbody><tr row-key="name"><th>Name</th><td>UserName1</td><td>UserName2</td></tr><tr row-key="age"><th>Age</th><td>25</td><td>30</td></tr></tbody></table></div>`);
242
+ });
243
+ (0, globals_1.it)("renders a table with pagination", () => {
244
+ const headers = [
245
+ { label: "Name", key: "name" },
246
+ { label: "Age", key: "age" },
247
+ ];
248
+ const rows = [
249
+ { name: "UserName1", age: 25, id: 1 },
250
+ { name: "UserName2", age: 30, id: 2 },
251
+ ];
252
+ const paginationOpts = {
253
+ current_page: 1,
254
+ pages: 3,
255
+ get_page_link: (page) => `?page=${page}`,
256
+ };
257
+ const result = (0, table_1.default)(headers, rows, { pagination: paginationOpts });
258
+ (0, globals_1.expect)(result).toBe(`<div class="table-responsive"><table class="table table-sm"><thead><tr><th>Name</th><th>Age</th></tr></thead><tbody><tr data-row-id="1"><td>UserName1</td><td>25</td></tr><tr data-row-id="2"><td>UserName2</td><td>30</td></tr></tbody></table><ul class="pagination"><li class="page-item active"><span class="page-link link-style" onclick="?page=1" role="link">1</span></li><li class="page-item"><span class="page-link link-style" onclick="?page=2" role="link">2</span></li><li class="page-item"><span class="page-link link-style" onclick="?page=3" role="link">3</span></li></ul></div>`);
259
+ });
260
+ });
261
+ (0, globals_1.describe)("mjml-layout", () => {
262
+ (0, globals_1.describe)("transformTextStyle", () => {
263
+ (0, globals_1.it)("transforms text styles correctly", () => {
264
+ (0, globals_1.expect)((0, mjml_layout_1.transformTextStyle)("h1")).toEqual({
265
+ "font-size": "2em",
266
+ "margin-top": "0.67em",
267
+ "margin-bottom": "0.67em",
268
+ "margin-left": 0,
269
+ "margin-right": 0,
270
+ "font-weight": "bold",
271
+ });
272
+ (0, globals_1.expect)((0, mjml_layout_1.transformTextStyle)("fw-bold")).toEqual({
273
+ "font-weight": "700 !important",
274
+ });
275
+ (0, globals_1.expect)((0, mjml_layout_1.transformTextStyle)("text-muted")).toEqual({
276
+ "--bs-text-opacity": "1",
277
+ color: "#858796 !important",
278
+ });
279
+ });
280
+ (0, globals_1.it)("returns an empty object for unknown styles", () => {
281
+ (0, globals_1.expect)((0, mjml_layout_1.transformTextStyle)("unknown-style")).toEqual({});
282
+ });
283
+ });
284
+ (0, globals_1.describe)("transformLinkSize", () => {
285
+ (0, globals_1.it)("transforms link sizes correctly", () => {
286
+ (0, globals_1.expect)((0, mjml_layout_1.transformLinkSize)("btn-lg")).toEqual({
287
+ padding: "0.5rem 1rem",
288
+ "font-size": "1.25rem",
289
+ });
290
+ (0, globals_1.expect)((0, mjml_layout_1.transformLinkSize)("btn-sm")).toEqual({
291
+ padding: "0.25rem 0.5rem",
292
+ "font-size": "0.875rem",
293
+ });
294
+ });
295
+ (0, globals_1.it)("returns an empty object for unknown sizes", () => {
296
+ (0, globals_1.expect)((0, mjml_layout_1.transformLinkSize)("unkown-size")).toEqual({});
297
+ });
298
+ });
299
+ (0, globals_1.describe)("applyTextStyle", () => {
300
+ (0, globals_1.it)("applies text style to block elements", () => {
301
+ const segment = {
302
+ textStyle: "h1",
303
+ inline: false,
304
+ style: {
305
+ color: "red",
306
+ },
307
+ };
308
+ const inner = "Hello World";
309
+ const result = (0, mjml_layout_1.applyTextStyle)(segment, inner);
310
+ (0, globals_1.expect)(result).toContain('<div style="');
311
+ (0, globals_1.expect)(result).toContain("font-size:2em");
312
+ (0, globals_1.expect)(result).toContain("color:red");
313
+ });
314
+ (0, globals_1.it)("applies text styles to inline elements", () => {
315
+ const segment = {
316
+ textStyle: "fw-bold",
317
+ inline: true,
318
+ style: { color: "blue" },
319
+ };
320
+ const inner = "Hello World";
321
+ const result = (0, mjml_layout_1.applyTextStyle)(segment, inner);
322
+ (0, globals_1.expect)(result).toContain('<span style="');
323
+ (0, globals_1.expect)(result).toContain("font-weight:700 !important");
324
+ (0, globals_1.expect)(result).toContain("color:blue");
325
+ (0, globals_1.expect)(result).toContain("Hello World");
326
+ });
327
+ (0, globals_1.it)("returns inner content without wrapping if no styles are applied", () => {
328
+ const segment = { textStyle: null, style: {} };
329
+ const inner = "Hello World";
330
+ const result = (0, mjml_layout_1.applyTextStyle)(segment, inner);
331
+ (0, globals_1.expect)(result).toBe("Hello World");
332
+ });
333
+ });
334
+ (0, globals_1.describe)("renderMJML", () => {
335
+ (0, globals_1.it)("renders a layout with an image", () => {
336
+ const layout = {
337
+ type: "container",
338
+ contents: [
339
+ {
340
+ type: "image",
341
+ alt: "Test Image",
342
+ fileid: 1,
343
+ srcType: "File",
344
+ style: {
345
+ width: "100%",
346
+ },
347
+ },
348
+ ],
349
+ };
350
+ const req = {
351
+ isSubView: false,
352
+ get_base_url: () => "https://example.com",
353
+ };
354
+ const result = (0, mjml_layout_1.renderMJML)({
355
+ layout,
356
+ req,
357
+ });
358
+ (0, globals_1.expect)(result.markup).toBe(`<mj-section><mj-raw><div style="text-align: left !important; font-size: 16px;"><img style="width:100%" alt="Test Image" src="https://example.com/files/serve/1"></div></mj-raw></mj-section>`);
359
+ });
360
+ (0, globals_1.it)("renders a layout with a link", () => {
361
+ const layout = {
362
+ type: "container",
363
+ contents: [
364
+ {
365
+ type: "link",
366
+ url: "https://example.com",
367
+ text: "Click Here",
368
+ link_style: "btn btn-primary",
369
+ },
370
+ ],
371
+ };
372
+ const result = (0, mjml_layout_1.renderMJML)({ layout, req: { isSubView: false } });
373
+ const expected = `<mj-section><mj-raw><div style="text-align: left !important; font-size: 16px;"><a rel="noopener" target="_blank" href="https://example.com" style="background-color: #0d6efd; color: #ffffff; font-size: 18px; font-family: Helvetica, Arial, sans-serif; font-weight: bold; text-decoration: none; padding: 14px 20px; border-radius: 5px; display: inline-block; mso-padding-alt: 0;">
374
+ <!--[if mso]>
375
+ <i style="letter-spacing: 25px; mso-font-width: -100%; mso-text-raise: 30pt;">&nbsp;</i>
376
+ <![endif]-->
377
+ <span style="mso-text-raise: 15pt;">Click Here</span>
378
+ <!--[if mso]>
379
+ <i style="letter-spacing: 25px; mso-font-width: -100%;">&nbsp;</i>
380
+ <![endif]-->
381
+ </a></div></mj-raw></mj-section>`
382
+ .replace(/\s+/g, " ")
383
+ .trim();
384
+ (0, globals_1.expect)(result.markup.replace(/\s+/g, " ").trim()).toBe(expected);
385
+ });
386
+ (0, globals_1.it)("renders a layout with nested containers", () => {
387
+ const layout = {
388
+ type: "container",
389
+ contents: [
390
+ {
391
+ type: "container",
392
+ contents: [
393
+ {
394
+ type: "blank",
395
+ contents: "Nested Text",
396
+ bgColor: "#f0f0f0",
397
+ },
398
+ ],
399
+ },
400
+ ],
401
+ };
402
+ const result = (0, mjml_layout_1.renderMJML)({
403
+ layout,
404
+ req: { isSubView: false },
405
+ });
406
+ const expected = `<mj-section><mj-raw><div style="text-align: left !important; font-size: 16px;"><div style=" border: 0px none black;">Nested Text</div></div></mj-raw></mj-section>`
407
+ .replace(/\s+/g, " ")
408
+ .trim();
409
+ const recieved = result.markup.replace(/\s+/g, " ").trim();
410
+ (0, globals_1.expect)(recieved).toBe(expected);
411
+ });
412
+ (0, globals_1.it)("renders a layout with a card", () => {
413
+ const layout = {
414
+ type: "container",
415
+ contents: [
416
+ {
417
+ type: "card",
418
+ title: "Card Title",
419
+ contents: { type: "blank", contents: "Card Content" },
420
+ },
421
+ ],
422
+ };
423
+ const result = (0, mjml_layout_1.renderMJML)({
424
+ layout,
425
+ req: { isSubView: false },
426
+ });
427
+ const expected = `<mj-section><mj-raw><div style="text-align: left !important; font-size: 16px;"><div class="card mt-4 shadow"><div class="card-header"><h6 class="m-0 fw-bold text-primary">Card Title</h6></div><div class="card-body">Card Content</div></div></div></mj-raw></mj-section>`
428
+ .replace(/\s+/g, " ")
429
+ .trim();
430
+ const recieved = result.markup.replace(/\s+/g, " ").trim();
431
+ (0, globals_1.expect)(recieved).toBe(expected);
432
+ });
433
+ (0, globals_1.it)("renders a layout with tabs", () => {
434
+ const layout = {
435
+ type: "tabs",
436
+ contents: [
437
+ {
438
+ type: "tabs",
439
+ tabs: {
440
+ Tab1: "Content1",
441
+ Tab2: "Content2",
442
+ },
443
+ },
444
+ ],
445
+ };
446
+ const result = (0, mjml_layout_1.renderMJML)({
447
+ layout,
448
+ req: { isSubView: false },
449
+ });
450
+ const expected = `<mj-section><mj-raw><div style="text-align: left !important; font-size: 16px;"></div></mj-raw></mj-section>`
451
+ .replace(/\s+/g, " ")
452
+ .trim();
453
+ const recieved = result.markup.replace(/\s+/g, " ").trim();
454
+ (0, globals_1.expect)(recieved).toBe(expected);
455
+ });
456
+ });
457
+ (0, globals_1.it)("renders a layout with a page header", () => {
458
+ const layout = {
459
+ type: "container",
460
+ contents: [
461
+ {
462
+ type: "pageHeader",
463
+ title: "Welcome",
464
+ blurb: "This is a test page header",
465
+ },
466
+ ],
467
+ };
468
+ const result = (0, mjml_layout_1.renderMJML)({ layout, req: { isSubView: false } });
469
+ (0, globals_1.expect)(result.markup).toBe(`<mj-section><mj-raw><div style="text-align: left !important; font-size: 16px;"><h1>Welcome</h1><p>This is a test page header</p></div></mj-raw></mj-section>`);
470
+ });
471
+ (0, globals_1.it)("renders layout with a table", () => {
472
+ const layout = {
473
+ type: "container",
474
+ contents: [
475
+ {
476
+ type: "table",
477
+ rows: 2,
478
+ columns: 2,
479
+ contents: [
480
+ [
481
+ { type: "blank", contents: "Cell 1" },
482
+ { type: "blank", contents: "Cell 2" },
483
+ ],
484
+ [
485
+ { type: "blank", contents: "Cell 3" },
486
+ { type: "blank", contents: "Cell 4" },
487
+ ],
488
+ ],
489
+ },
490
+ ],
491
+ };
492
+ const result = (0, mjml_layout_1.renderMJML)({
493
+ layout,
494
+ req: { isSubView: false },
495
+ });
496
+ (0, globals_1.expect)(result.markup).toBe(`<mj-section><mj-raw><div style="text-align: left !important; font-size: 16px;"><table><tbody><tr><td>Cell 1</td><td>Cell 2</td></tr><tr><td>Cell 3</td><td>Cell 4</td></tr></tbody></table></div></mj-raw></mj-section>`);
497
+ });
498
+ (0, globals_1.it)("renders a layout with a dropdown menu", () => {
499
+ const layout = {
500
+ type: "container",
501
+ contents: [
502
+ {
503
+ type: "dropdown_menu",
504
+ items: [
505
+ {
506
+ type: "Option 1",
507
+ url: "/option1",
508
+ },
509
+ {
510
+ type: "Option 2",
511
+ url: "/option2",
512
+ },
513
+ ],
514
+ },
515
+ ],
516
+ };
517
+ const result = (0, mjml_layout_1.renderMJML)({ layout, req: { isSubView: false } });
518
+ (0, globals_1.expect)(result.markup).toBe(`<mj-section><mj-raw><div style="text-align: left !important; font-size: 16px;"></div></mj-raw></mj-section>`);
519
+ });
520
+ (0, globals_1.it)("renders a layout with conditional visibility based on role", () => {
521
+ const layout = {
522
+ type: "container",
523
+ contents: [
524
+ {
525
+ type: "blank",
526
+ contents: "Visible to all",
527
+ },
528
+ {
529
+ type: "blank",
530
+ contents: "Visible to admins only",
531
+ minRole: 1,
532
+ },
533
+ ],
534
+ };
535
+ const resultForAdmin = (0, mjml_layout_1.renderMJML)({
536
+ layout,
537
+ req: { isSubView: false },
538
+ role: 1,
539
+ });
540
+ const resultForUser = (0, mjml_layout_1.renderMJML)({
541
+ layout,
542
+ req: { isSubView: false },
543
+ role: 2,
544
+ });
545
+ (0, globals_1.expect)(resultForAdmin.markup).toContain(`<mj-section><mj-raw><div style="text-align: left !important; font-size: 16px;">Visible to allVisible to admins only</div></mj-raw></mj-section>`);
546
+ (0, globals_1.expect)(resultForUser.markup).toContain(`<mj-section><mj-raw><div style="text-align: left !important; font-size: 16px;">Visible to all</div></mj-raw></mj-section>`);
547
+ });
548
+ (0, globals_1.it)("renders a layout with a gradient background", () => {
549
+ const layout = {
550
+ type: "container",
551
+ bgType: "Gradient",
552
+ gradStartColor: "#ff0000",
553
+ gradEndColor: "#0000ff",
554
+ gradDirection: 45,
555
+ contents: [{ type: "blank", contents: "Gradient Background" }],
556
+ };
557
+ const result = (0, mjml_layout_1.renderMJML)({ layout, req: { isSubView: false } });
558
+ (0, globals_1.expect)(result.markup).toBe(`<mj-section><mj-raw><div style="text-align: left !important; font-size: 16px;">Gradient Background</div></mj-raw></mj-section>`);
559
+ });
560
+ (0, globals_1.it)("renders a layout with a rotated container", () => {
561
+ const layout = {
562
+ type: "container",
563
+ rotate: 45,
564
+ contents: [{ type: "blank", contents: "Rotated Content" }],
565
+ };
566
+ const result = (0, mjml_layout_1.renderMJML)({ layout, req: { isSubView: false } });
567
+ (0, globals_1.expect)(result.markup).toBe(`<mj-section><mj-raw><div style="text-align: left !important; font-size: 16px;">Rotated Content</div></mj-raw></mj-section>`);
568
+ });
569
+ (0, globals_1.it)("renders a layout with a custom class and CSS", () => {
570
+ const layout = {
571
+ type: "container",
572
+ customClass: "custom-container",
573
+ customCSS: "border: 1px solid red;",
574
+ contents: [{ type: "blank", contents: "Custom Styled Content" }],
575
+ };
576
+ const result = (0, mjml_layout_1.renderMJML)({ layout, req: { isSubView: false } });
577
+ (0, globals_1.expect)(result.markup).toBe(`<mj-section><mj-raw><div style="text-align: left !important; font-size: 16px;">Custom Styled Content</div></mj-raw></mj-section>`);
578
+ });
579
+ });
580
+ (0, globals_1.describe)("index", () => {
581
+ (0, globals_1.describe)("post-bn", () => {
582
+ (0, globals_1.it)("renders a basic post button", () => {
583
+ const result = post_btn("/submit", "Submit", "csrfToken123", {
584
+ btnClass: "btn-primary",
585
+ });
586
+ (0, globals_1.expect)(result).toContain('<form action="/submit" method="post">');
587
+ (0, globals_1.expect)(result).toContain('<input type="hidden" name="_csrf" value="csrfToken123">');
588
+ (0, globals_1.expect)(result).toContain('<button type="submit" class=" btn btn-primary d-inline-block">Submit</button>');
589
+ (0, globals_1.expect)(result).toContain("</form>");
590
+ });
591
+ (0, globals_1.it)("renders a basic post button", () => {
592
+ const result = post_btn("/submit", "Submit", "csrfToken123", {
593
+ btnClass: "btn-primary",
594
+ });
595
+ (0, globals_1.expect)(result).toContain('<form action="/submit" method="post">');
596
+ (0, globals_1.expect)(result).toContain('<input type="hidden" name="_csrf" value="csrfToken123">');
597
+ (0, globals_1.expect)(result).toContain('<button type="submit" class=" btn btn-primary d-inline-block">Submit</button>');
598
+ (0, globals_1.expect)(result).toContain("</form>");
599
+ });
600
+ });
601
+ (0, globals_1.describe)("post_delete_btn", () => {
602
+ (0, globals_1.it)("renders a delete button with confirmation", () => {
603
+ const req = {
604
+ csrfToken: () => "csrfToken123",
605
+ __: (str) => str,
606
+ "Are you sure?": "Are you sure?",
607
+ };
608
+ const result = post_delete_btn("/delete", req);
609
+ const normalized = result.replace(/\s+/g, " ").trim();
610
+ (0, globals_1.expect)(normalized).toBe(`<form action=\"/delete\" method=\"post\"><input type=\"hidden\" name=\"_csrf\" value=\"csrfToken123\"><button type=\"submit\" class=\"btn btn-danger btn-sm\" onclick=\"return confirm('Are you sure?')\"><i class=\"fas fa-trash-alt\"></i></button></form>`
611
+ .replace(/\s+/g, " ")
612
+ .trim());
613
+ });
614
+ });
615
+ (0, globals_1.describe)("post_dropdown_item", () => {
616
+ (0, globals_1.it)("renders a dropdown item with confirmation", () => {
617
+ const req = {
618
+ csrfToken: () => "csrfToken123",
619
+ __: (str) => str,
620
+ "Are you sure?": "Are you sure?",
621
+ };
622
+ const result = post_dropdown_item("/delete", "Delete", req, true);
623
+ (0, globals_1.expect)(result).toContain('<a class="dropdown-item"');
624
+ (0, globals_1.expect)(result).toContain("onclick=\"if(confirm('Are you sure?'))");
625
+ (0, globals_1.expect)(result).toContain("$('#delete').submit()\">Delete</a>");
626
+ (0, globals_1.expect)(result).toContain('<form id="delete" action="/delete" method="post">');
627
+ (0, globals_1.expect)(result).toContain('<input type="hidden" name="_csrf" value="csrfToken123">');
628
+ });
629
+ });
630
+ (0, globals_1.describe)("settingsDropdown", () => {
631
+ (0, globals_1.it)("renders a settings dropdown", () => {
632
+ const result = settingsDropdown("dropdown1", "<a>Option 1</a>");
633
+ (0, globals_1.expect)(result).toContain('<div class="dropdown">');
634
+ (0, globals_1.expect)(result).toContain('<button class="btn btn-sm btn-outline-secondary"');
635
+ (0, globals_1.expect)(result).toContain('<div class="dropdown-menu dropdown-menu-end"');
636
+ (0, globals_1.expect)(result).toContain("<a>Option 1</a>");
637
+ });
638
+ });
639
+ (0, globals_1.describe)("localeDate", () => {
640
+ (0, globals_1.it)("renders a localized date", () => {
641
+ const date = new Date("2023-01-01T00:00:00Z");
642
+ const result = localeDate(date, {}, "en");
643
+ (0, globals_1.expect)(result).toContain('<time datetime="2023-01-01T00:00:00.000Z"');
644
+ (0, globals_1.expect)(result).toContain(">1/1/2023</time>");
645
+ });
646
+ (0, globals_1.it)("renders a localized date with custom options", () => {
647
+ const date = new Date("2023-01-01T00:00:00Z");
648
+ const result = localeDate(date, { year: "numeric", month: "long", day: "numeric" }, "en");
649
+ (0, globals_1.expect)(result).toContain('<time datetime="2023-01-01T00:00:00.000Z"');
650
+ (0, globals_1.expect)(result).toContain(">January 1, 2023</time>");
651
+ });
652
+ });
653
+ });
654
+ (0, globals_1.describe)("builder", () => {
655
+ (0, globals_1.it)("renders the builder with default options", () => {
656
+ const options = { someOption: "value" };
657
+ const context = { someContent: "value" };
658
+ const action = "/submit";
659
+ const stepName = "step1";
660
+ const layout = { type: "container", contents: [] };
661
+ const csrfToken = "csrfToken123";
662
+ const result = (0, builder_1.default)({ options, context, action, stepName, layout }, csrfToken);
663
+ (0, globals_1.expect)(result).toContain('<div id="saltcorn-builder"></div>');
664
+ (0, globals_1.expect)(result).toContain('<form action="/submit" method="post" id="scbuildform">');
665
+ (0, globals_1.expect)(result).toContain('<input type="hidden" name="contextEnc"');
666
+ (0, globals_1.expect)(result).toContain('<input type="hidden" name="stepName" value="step1">');
667
+ (0, globals_1.expect)(result).toContain('<input type="hidden" name="_csrf" value="csrfToken123"');
668
+ (0, globals_1.expect)(result).toContain("builder.renderBuilder(");
669
+ });
670
+ (0, globals_1.it)("renders the builder with a version tag", () => {
671
+ const options = { someOptions: "value" };
672
+ const context = { someContext: "value" };
673
+ const action = "/submit";
674
+ const stepName = "step1";
675
+ const layout = { type: "container", contents: [] };
676
+ const csrfToken = "csrfToken123";
677
+ const version_tag = "v1.0.0";
678
+ const result = (0, builder_1.default)({ options, context, action, stepName, layout, version_tag }, csrfToken);
679
+ (0, globals_1.expect)(result).toContain('<script src="/static_assets/v1.0.0/builder_bundle.js"></script>');
680
+ (0, globals_1.expect)(result).toContain('<link rel="stylesheet" type="text/css" media="screen" href="/static_assets/v1.0.0/saltcorn-builder.css">');
681
+ });
682
+ (0, globals_1.it)("renders the builder with custom mode", () => {
683
+ const options = { someOption: "value" };
684
+ const context = { someOptions: "value" };
685
+ const action = "/submit";
686
+ const stepName = "step1";
687
+ const layout = { type: "container", contents: [] };
688
+ const csrfToken = "csrfToken123";
689
+ const mode = "edit";
690
+ const result = (0, builder_1.default)({ options, context, action, stepName, layout, mode }, csrfToken);
691
+ (0, globals_1.expect)(result).toContain("builder.renderBuilder(");
692
+ (0, globals_1.expect)(result).toContain('"edit"');
693
+ });
694
+ });
695
+ //# sourceMappingURL=markup.test.js.map