Skip to content

Commit d158b67

Browse files
authored
Merge pull request #31 from tinymce/feature/INT-2955
INT-2955: Support finding a surrounding form when in nested shadow dom
2 parents 6cf0658 + 5cc4891 commit d158b67

File tree

3 files changed

+48
-6
lines changed

3 files changed

+48
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## Unreleased
88
### Fixed
9+
- Find an associated form even when in a nested shadow DOM.
910
- Updated dependencies to latest version
1011

1112
## 2.0.0

src/demo/ts/Server.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,28 +19,52 @@ const encodeHtmlEntities = (value: string) => value.replace(/&/g, '&').repla
1919
* @param {string} editor2Value the value of the second editor.
2020
* @return {string} the page HTML.
2121
*/
22-
const page = (editor1Value: string, editor2Value: string) => `
22+
const page = (editor1Value: string, editor2Value: string, editor3Value: string, editor4Value: string) => `
2323
<!DOCTYPE html>
2424
<html lang="en">
2525
<head>
2626
<meta charset="utf-8"/>
2727
<title>TinyMCE WebComponent Form Demo Page</title>
2828
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
29+
<script>
30+
class TinyMceEditorNested extends HTMLElement {
31+
connectedCallback() {
32+
const count = parseInt(this.getAttribute('nested') || '1', 10);
33+
const content = this.getAttribute('value');
34+
const shadow = this.attachShadow({ mode: 'open' });
35+
const target = document.createElement('tinymce-editor' + (count > 1 ? '-nested' : ''));
36+
[...this.attributes].forEach( attr => { target.setAttribute(attr.nodeName, attr.nodeValue) });
37+
target.setAttribute('nested', count - 1);
38+
target.appendChild(document.createTextNode(content));
39+
shadow.appendChild(target);
40+
}
41+
}
42+
window.customElements.define('tinymce-editor-nested', TinyMceEditorNested);
43+
</script>
2944
</head>
3045
<body>
3146
<h1>TinyMCE WebComponent in Form</h1>
3247
<h2>Editor 1 (outside form with form attribute)</h2>
3348
<tinymce-editor name="editor1" form="myform">${encodeHtmlEntities(editor1Value)}</tinymce-editor>
34-
<h2>Editor 2 (inside form)</h2>
49+
<h2>Editor 2 (nested in shadow dom, outside form with form attribute)</h2>
50+
<tinymce-editor-nested nested="2" name="editor2" form="myform" value="${encodeHtmlEntities(editor2Value)}"></tinymce-editor-nested>
3551
<form id="myform" method="POST" action="/">
36-
<tinymce-editor name="editor2">${encodeHtmlEntities(editor2Value)}</tinymce-editor>
52+
<h2>Editor 3 (inside form)</h2>
53+
<tinymce-editor name="editor3">${encodeHtmlEntities(editor3Value)}</tinymce-editor>
54+
<h2>Editor 4 (nested in shadow dom, inside form)</h2>
55+
<tinymce-editor-nested nested="2" name="editor4" value="${encodeHtmlEntities(editor4Value)}"></tinymce-editor-nested>
3756
<input type="submit" value="Submit">
3857
</form>
58+
3959
<h2>Posted Content</h2>
4060
<h3>Editor 1 value</h3>
4161
<div style="border: 1px solid black">${editor1Value}</div>
4262
<h3>Editor 2 value</h3>
4363
<div style="border: 1px solid black">${editor2Value}</div>
64+
<h3>Editor 3 value</h3>
65+
<div style="border: 1px solid black">${editor3Value}</div>
66+
<h3>Editor 4 value</h3>
67+
<div style="border: 1px solid black">${editor4Value}</div>
4468
<script src="/tinymce/tinymce.js"></script>
4569
<script src="/dist/tinymce-webcomponent.js"></script>
4670
</body>
@@ -57,12 +81,12 @@ app.use('/tinymce', express.static(tinyPath));
5781
app.use('/dist', express.static(distPath));
5882

5983
app.get('/', (request, response) => {
60-
response.send(page('', ''));
84+
response.send(page('', '', '', ''));
6185
});
6286

6387
// Access the parse results as request.body
6488
app.post('/', (request, response) => {
65-
response.send(page(request.body.editor1 as string, request.body.editor2 as string));
89+
response.send(page(request.body.editor1 as string, request.body.editor2 as string, request.body.editor3 as string, request.body.editor4 as string));
6690
});
6791

6892
app.listen(3000, () => console.log('http://localhost:3000/'));

src/main/ts/component/Editor.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,23 @@ enum Status {
1414
Ready
1515
}
1616

17+
// handle traversing all shadow roots
18+
const closestRecursive: {
19+
<K extends keyof HTMLElementTagNameMap>(selector: K, element: Element): HTMLElementTagNameMap[K] | null;
20+
<K extends keyof SVGElementTagNameMap>(selector: K, element: Element): SVGElementTagNameMap[K] | null;
21+
<E extends Element = Element>(selectors: string, element: Element): E | null;
22+
} = (selector: string, element: Element): Element | null => {
23+
const found = element.closest(selector);
24+
if (found !== null) {
25+
return found;
26+
}
27+
const next = (element.getRootNode() as ShadowRoot).host;
28+
if (next !== null && next !== undefined) {
29+
return closestRecursive(selector, next);
30+
}
31+
return null;
32+
};
33+
1734
const parseJsonResolveGlobals = (value: string): unknown => {
1835
try {
1936
return JSON.parse(value);
@@ -142,7 +159,7 @@ class TinyMceEditor extends HTMLElement {
142159
private _updateForm(): void {
143160
if (this.isConnected) {
144161
const formId = this.getAttribute('form');
145-
const form = formId !== null ? this.ownerDocument.querySelector<HTMLFormElement>('form#' + formId) : this.closest('form');
162+
const form = formId !== null ? this.ownerDocument.querySelector<HTMLFormElement>('form#' + formId) : closestRecursive('form', this);
146163
if (this._form !== form) {
147164
if (this._form !== null) {
148165
this._form.removeEventListener('formdata', this._formDataHandler);

0 commit comments

Comments
 (0)