Skip to content

Commit a9ba471

Browse files
authored
Merge pull request #1104 from Patternslib/fix-csr-problem
Fix csr problem
2 parents 5588f36 + 028ba07 commit a9ba471

File tree

9 files changed

+82
-124
lines changed

9 files changed

+82
-124
lines changed

src/core/dom.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
/* Utilities for DOM traversal or navigation */
22
import events from "./events";
3+
import logging from "./logging";
4+
5+
const logger = logging.getLogger("core dom");
36

47
const DATA_PREFIX = "__patternslib__data_prefix__";
58
const DATA_STYLE_DISPLAY = "__patternslib__style__display";
@@ -309,6 +312,10 @@ const delete_data = (el, name) => {
309312
/**
310313
* Simple template engine, based on JS template literal
311314
*
315+
* NOTE: This uses eval and would break if Content-Security-Policy does not
316+
* allow 'unsafe-eval'.
317+
* Because of this CSR problem the use of this method is not recommended.
318+
*
312319
* Please note: You cannot pass a template literal as template_string.
313320
* JavaScript itself would try to expand it and would fail.
314321
*
@@ -323,6 +330,9 @@ const delete_data = (el, name) => {
323330
* @returns {String} - Returns the a string as template expanded with the template_variables.
324331
*/
325332
const template = (template_string, template_variables = {}) => {
333+
logger.warn(
334+
"Using dom.template is not recommended due to a problem with Content-Security-Policy."
335+
);
326336
return new Function("return `" + template_string + "`;").call(template_variables);
327337
};
328338

src/pat/clone-code/clone-code.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
import { BasePattern } from "../../core/basepattern";
2-
import code_wrapper_template from "./templates/code-wrapper.html";
3-
import dom from "../../core/dom";
42
import Parser from "../../core/parser";
53
import registry from "../../core/registry";
64
import utils from "../../core/utils";
@@ -63,17 +61,24 @@ class Pattern extends BasePattern {
6361
});
6462
}
6563

66-
markup = utils.escape_html(markup);
67-
const pre_code_markup = dom.template(code_wrapper_template, { markup: markup });
68-
6964
// Now we need to wrap the contents in any case in a div.
7065
tmp_wrapper = document.createElement("div");
71-
tmp_wrapper.innerHTML = pre_code_markup;
66+
tmp_wrapper.innerHTML = this.wrapper_template(utils.escape_html(markup));
7267
const pre_code_el = tmp_wrapper.children[0];
7368

7469
this.el.appendChild(pre_code_el);
7570
registry.scan(pre_code_el);
7671
}
72+
73+
wrapper_template(markup) {
74+
return `
75+
<pre class="pat-syntax-highlight">
76+
<code class="language-html">
77+
${markup}
78+
</code>
79+
</pre>
80+
`;
81+
}
7782
}
7883

7984
registry.register(Pattern);
Lines changed: 22 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,43 @@
11
## Description
22

3-
The clone pattern lets the website user clone elements in the page.
3+
Copy part of the DOM tree and show it as code.
4+
45

56
## Documentation
67

7-
The clone pattern is typically used in case you want to create a form on which it is unknown how many instances the user will need of a certain field or group of fields.
8-
For instance if you want to ask the user to fill out the name and birthdate of each family member.
8+
The clone-code copies a part of the DOM tree and wraps it in a `<pre><code>` structure as plain text.
9+
10+
This pattern is especially useful when writing interactive documentation and showing the code at the same time.
11+
912

1013
### Usage
1114

1215
This pattern is enabled by adding the `pat-clone` class on a container element which contains the original element and any clones of it that may have beeen added.
1316
The first element inside the .pat-clone container is by default assumed to be the original element may be cloned by the user.
1417

15-
Consider the following markup:
16-
17-
<h3>List of family members</h3>
18-
19-
<div class="pat-clone">
20-
<!-- The first element inside the .pat-clone container is by default
21-
assumed to be the original element which will be cloned.
22-
-->
23-
<fieldset class="clone"> <!-- By default, pat-clone will consider elements with the "clone" class to be clones. -->
24-
<legend>Family member 1</legend>
25-
<input name="name-1" type="text" placeholder="Name" />
26-
<input name="date-1" type="date" placeholder="birthdate" /><br/>
27-
<button type="button" class="remove-clone">Remove</button>
28-
</fieldset>
29-
<!-- Now comes the clone trigger, a button which when clicked will cause
30-
a new clone of the above fieldset to be created.
31-
-->
32-
<button type="button" class="add-clone">Add an extra family member</button>
18+
<div class="pat-clone-code">
19+
<p>Hello.</p>
3320
</div>
3421

35-
Each time the user clicks on the button saying 'Add an extra family member', the
36-
pattern will make a copy of the first element inside the
37-
`.pat-clone` element, unless the `template` property is used to configure a
38-
different clone template. The `template` property takes a CSS selector as
39-
value.
40-
41-
Typically when using a template element, such an element would be hidden from view.
42-
43-
The new clone is always appended at the end, inside the `.pat-clone` element.
22+
This will result in:
4423

45-
When creating a `.pat-clone` element containing existing clones, it's
46-
important that each existing clone either gets the `clone` CSS class or that you
47-
pass in a unique CSS selector for each clone via the `clone-element`
48-
property. This allows the pattern to properly determine how many existing
49-
clones there are to start with.
24+
<pre class="pat-syntax-highlight language-html">
25+
<code class="language-html hljs">
26+
<span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Hello.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
27+
</code>
28+
</pre>
5029

51-
#### Incrementation of values in clones
52-
53-
The pat-clone pattern will automatically add up any number in the values of name and value attributes.
54-
For instance, if you have `name="item-1"` in your markup, then the first clone will be
55-
`name="item-2"` and the one after that `name="item-3"` etc.. If you want to print a number
56-
— for instance in a header of each clone — then you can use the syntax: `#{1}`. This string
57-
will be replaced by an number that's also increased by 1 for each clone.
58-
59-
### Example with a hidden template
60-
61-
The markup below would have exactly the same effect as the first example, but using a hidden template. This might come in handy when the first instance shown should either contain different information, or if it will have pre-filled values by the server.
62-
63-
<h3>List of family members</h3>
64-
65-
<div class="pat-clone" data-pat-clone="template: #my-template">
66-
<!-- First visible instance and also template -->
67-
<fieldset class="clone">
68-
<legend>Family member 1</legend>
69-
<input name="Mary Johnson" type="text" placeholder="Name" />
70-
<input name="1977-04-16" type="date" placeholder="birthdate" /><br/>
71-
<button type="button" class="remove-clone">Remove</button>
72-
</fieldset>
73-
74-
<!-- Template markup -->
75-
<fieldset id="my-template" hidden>
76-
<legend>Family member #{1}</legend>
77-
<input name="name-1" type="text" placeholder="Name" />
78-
<input name="date-1" type="date" placeholder="birthdate" /><br/>
79-
<button type="button" class="remove-clone">Remove</button>
80-
</fieldset>
81-
82-
<!-- Clone trigger -->
83-
<button type="button" class="add-clone">Add an extra family member</button>
84-
</div>
85-
86-
### Example with a hidden template which includes a pattern
87-
88-
Patterns in templates are initialized after cloning.
89-
However, the patterns in the template itself are not initialized if the template has the attribute ``hidden`` or the class ``disable-patterns``.
90-
This is to prevent double-initialization within the template and after being cloned.
91-
92-
<div id="template" class="disable-patterns" hidden>
93-
<input name="date-1" type="date" class="pat-date-picker" />
94-
</fieldset>
95-
96-
<div class="pat-clone" data-pat-clone="template: #template">
97-
<button type="button" class="add-clone">Add a date</button>
98-
</div>
9930

31+
### Excluding markup from copying
10032

33+
You can exclude markup from copying by adding the `clone-code` class to it.
34+
If that class is present, the whole `clone-code` markup subtree is ignored.
10135

10236

10337
### Option reference
10438

105-
| Property | Description | Default | Allowed Values | Type |
106-
| ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ----------------- | --------------------------------- |
107-
| template | Selects the element that will be cloned each time. You might often want to refer to a piece of template markup for this that is hidden with though the CSS. | :first | | CSS Selector |
108-
| max | Maximum number of clones that is allowed | | | Integer |
109-
| trigger-element | Selector of the element that will add the clone when clicked upon. | .add-clone | | CSS Selector |
110-
| remove-element | Selector of the element that will remove the clone when clicked upon. | .remove-clone | | CSS Selector |
111-
| remove-behaviour or remove-behavior | What should happen when the user clicks on the element to remove a clone? Two choices exist currently. Show a confirmation dialog, or simply remove the clone immediately. | "confirm" | "confirm", "none" | One of the allowed string values. |
112-
| clone-element | Selector of the individual clone element(s). | .clone | | CSS Selector |
39+
| Property | Description | Default | Type |
40+
| ---------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ------------- | --------------------------------- |
41+
| source | CSS selector to define the source from where the markup should be copied. | :first-child | CSS Selector |
42+
| features | List of features to activate. Currently only `format` is implemented. Format does prettify the HTML markup with the library `prettier`. | null | Comma seperated list of strings. |
43+

src/pat/clone-code/index.html

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,29 @@
1111
</head>
1212
<body>
1313

14-
<h1>Example - clone-code</h1>
14+
<h1>Clone Code</h1>
1515

16-
<p class="clone-ignore">Demo on how to use pat-clone together with pat-syntax-highlight to show the source of any html snippet</p>
16+
<h2>Example 1</h2>
17+
18+
<p class="clone-ignore">
19+
Clone the next block and ignore this paragraph which has the `clone-ignore` class.
20+
</p>
21+
22+
<div class="pat-clone-code">
23+
<p>Hello.</p>
24+
</div>
25+
26+
<h2>Example 2</h2>
27+
28+
<p class="clone-ignore">
29+
Clone the whole HTML page but ignore this paragraph which has the `clone-ignore` class.
30+
</p>
1731

1832
<div class="pat-clone-code"
1933
data-pat-clone-code="source: html">
2034
<p>Test paragraph</p>
2135
</div>
2236

37+
2338
</body>
2439
</html>

src/pat/clone-code/templates/code-wrapper.html

Lines changed: 0 additions & 5 deletions
This file was deleted.

src/pat/validation/documentation.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,3 @@ Error messages can also be overridden on a per-field basis, for example:
7979
| message-required | The error message for required fields. | This field is required. | String |
8080
| not-after | Field-specific. A lower time limit restriction for date and datetime fields. | | CSS Selector or a ISO8601 date string. |
8181
| not-before | Field-specific. An upper time limit restriction for date and datetime fields. | | CSS Selector or a ISO8601 date string. |
82-
| error-template | The template to be used to render the error message. | &lt;em class="validation warning message"&gt;${this.message}&lt;/em&gt; | String |
83-

src/pat/validation/validation.js

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,10 @@ parser.addArgument("not-before", null);
2626
parser.addArgument("equality", null);
2727
parser.addArgument("delay", 100); // Delay before validation is done to avoid validating while typing.
2828

29-
// Template for the validation message
30-
parser.addArgument(
31-
"error-template",
32-
'<em class="validation warning message">${this.message}</em>'
33-
);
34-
3529
// BBB
3630
// TODO: deprecated. Will be removed with next major version.
3731
parser.addAlias("message-integer", "message-number");
32+
parser.addArgument("error-template");
3833

3934
const KEY_ERROR_EL = "__patternslib__input__error__el";
4035
const KEY_ERROR_MSG = "__patternslib__input__error__msg";
@@ -324,7 +319,7 @@ export default Base.extend({
324319
event.stopPropagation();
325320
event.stopImmediatePropagation();
326321
}
327-
this.set_error_message(input, input_options);
322+
this.set_error_message(input);
328323
},
329324

330325
set_validity({ input, msg, attribute = null, min = null, max = null }) {
@@ -372,7 +367,7 @@ export default Base.extend({
372367
}
373368
},
374369

375-
set_error_message(input, options) {
370+
set_error_message(input) {
376371
this.remove_error(input);
377372

378373
// Do not set a error message for a input group like radio buttons or
@@ -385,10 +380,9 @@ export default Base.extend({
385380

386381
// Create the validation error DOM node from the template
387382
const validation_message = input.validationMessage || input[KEY_ERROR_MSG];
388-
const error_template = dom.template(options.errorTemplate, {
389-
message: validation_message,
390-
});
391-
const error_node = dom.create_from_string(error_template).firstChild;
383+
const error_node = dom.create_from_string(
384+
this.error_template(validation_message)
385+
).firstChild;
392386

393387
let fieldset;
394388
if (input.type === "radio" || input.type === "checkbox") {
@@ -423,4 +417,9 @@ export default Base.extend({
423417
}
424418
}
425419
},
420+
421+
error_template(message) {
422+
// Template for the validation message
423+
return `<em class="validation warning message">${message}</em>`;
424+
},
426425
});

src/pat/validation/validation.test.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -215,20 +215,23 @@ describe("pat-validation", function () {
215215
expect(el.querySelector("#form-buttons-create").disabled).toBe(false);
216216
});
217217

218-
it("1.9 - can define a custom error message template.", async function () {
219-
const _msg = "${this.message}"; // need to define the template in normal quotes here to not make the parser expand the missing variable.
218+
it("1.9 - can define a custom error message template by subclassing.", async function () {
219+
class CustomValidation extends Pattern {
220+
error_template(message) {
221+
return `<div class="validation-error">${message}</div>`;
222+
}
223+
}
224+
220225
document.body.innerHTML = `
221226
<form class="pat-validation"
222-
data-pat-validation='
223-
message-required: need this;
224-
error-template: &lt;div class="validation-error"&gt;${_msg}&lt;/div&gt;'>
227+
data-pat-validation="message-required: need this;">
225228
<input type="text" name="name" required>
226229
</form>
227230
`;
228231
const el = document.querySelector(".pat-validation");
229232
const inp = el.querySelector("[name=name]");
230233

231-
new Pattern(el);
234+
new CustomValidation(el);
232235
await utils.timeout(1); // wait a tick for async to settle.
233236

234237
inp.value = "";

webpack/webpack.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ module.exports = () => {
6060

6161
if (process.env.NODE_ENV === "development") {
6262
config.devServer.static.directory = path.resolve(__dirname, "../");
63+
config.devServer.headers["Content-Security-Policy"] =
64+
"default-src https: http: data: 'self' 'unsafe-inline'; script-src https: http: data: 'self' 'unsafe-inline';";
6365
}
6466

6567
// Add an @patternslib/patternslib alias when building within this repository.

0 commit comments

Comments
 (0)