kopia lustrzana https://github.com/wagtail/wagtail
Refactor BoundWidget to accept an iterable of elements
rodzic
0cc274f36a
commit
0ae74677b2
|
@ -3,18 +3,38 @@ import { runInlineScripts } from '../../../utils/runInlineScripts';
|
|||
|
||||
class BoundWidget {
|
||||
constructor(
|
||||
element,
|
||||
elementOrNodeList,
|
||||
name,
|
||||
idForLabel,
|
||||
initialState,
|
||||
parentCapabilities,
|
||||
options,
|
||||
) {
|
||||
// if elementOrNodeList not iterable, it must be a single element
|
||||
const nodeList = elementOrNodeList.forEach
|
||||
? elementOrNodeList
|
||||
: [elementOrNodeList];
|
||||
|
||||
// look for an input element with the given name, as either a direct element of nodeList
|
||||
// or a descendant
|
||||
const selector = `:is(input,select,textarea,button)[name="${name}"]`;
|
||||
// find, including element itself
|
||||
this.input = element.matches(selector)
|
||||
? element
|
||||
: element.querySelector(selector);
|
||||
|
||||
for (let i = 0; i < nodeList.length; i += 1) {
|
||||
const element = nodeList[i];
|
||||
if (element.nodeType === Node.ELEMENT_NODE) {
|
||||
if (element.matches(selector)) {
|
||||
this.input = element;
|
||||
break;
|
||||
} else {
|
||||
const input = element.querySelector(selector);
|
||||
if (input) {
|
||||
this.input = input;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.idForLabel = idForLabel;
|
||||
this.setState(initialState);
|
||||
this.parentCapabilities = parentCapabilities || new Map();
|
||||
|
@ -71,27 +91,33 @@ class Widget {
|
|||
const html = this.html.replace(/__NAME__/g, name).replace(/__ID__/g, id);
|
||||
const idForLabel = this.idPattern.replace(/__ID__/g, id);
|
||||
|
||||
/* write the HTML into a temp container to parse it into an element */
|
||||
/* write the HTML into a temp container to parse it into a node list */
|
||||
const tempContainer = document.createElement('div');
|
||||
tempContainer.innerHTML = html.trim();
|
||||
const dom = tempContainer.firstChild;
|
||||
const childNodes = Array.from(tempContainer.childNodes);
|
||||
|
||||
/* replace the placeholder with the new nodes */
|
||||
placeholder.replaceWith(...childNodes);
|
||||
|
||||
const childElements = childNodes.filter(
|
||||
(node) => node.nodeType === Node.ELEMENT_NODE,
|
||||
);
|
||||
|
||||
/* execute any scripts in the new element(s) */
|
||||
runInlineScripts(tempContainer);
|
||||
|
||||
/* replace the placeholder with the new element(s) */
|
||||
placeholder.replaceWith(...tempContainer.childNodes);
|
||||
childElements.forEach((element) => {
|
||||
runInlineScripts(element);
|
||||
});
|
||||
|
||||
// Add any extra attributes we received to the first element of the widget
|
||||
if (typeof options?.attributes === 'object') {
|
||||
Object.entries(options.attributes).forEach(([key, value]) => {
|
||||
dom.setAttribute(key, value);
|
||||
childElements[0].setAttribute(key, value);
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line new-cap
|
||||
return new this.boundWidgetClass(
|
||||
dom,
|
||||
childElements.length === 1 ? childElements[0] : childNodes,
|
||||
name,
|
||||
idForLabel,
|
||||
initialState,
|
||||
|
|
|
@ -133,7 +133,7 @@ describe('telepath: wagtail.widgets.Widget with multiple top-level nodes', () =>
|
|||
widgetDef = window.telepath.unpack({
|
||||
_type: 'wagtail.widgets.Widget',
|
||||
_args: [
|
||||
'<input type="text" name="__NAME__" maxlength="255" id="__ID__"><button data-button-state="idle">Click me</button><script>document.getElementById("__ID__").className = "custom-class";</script>',
|
||||
'<!-- here comes a widget --><input type="text" name="__NAME__" maxlength="255" id="__ID__"><button data-button-state="idle">Click me</button><script>document.getElementById("__ID__").className = "custom-class";</script>',
|
||||
'__ID__',
|
||||
],
|
||||
});
|
||||
|
|
|
@ -1,20 +1,27 @@
|
|||
/**
|
||||
* Runs any inline scripts contained within the given DOM element or fragment.
|
||||
*/
|
||||
const runScript = (script: HTMLScriptElement) => {
|
||||
if (!script.type || script.type === 'application/javascript') {
|
||||
const newScript = document.createElement('script');
|
||||
Array.from(script.attributes).forEach((key) =>
|
||||
newScript.setAttribute(key.nodeName, key.nodeValue || ''),
|
||||
);
|
||||
newScript.text = script.text;
|
||||
script.replaceWith(newScript);
|
||||
}
|
||||
};
|
||||
|
||||
const runInlineScripts = (element: HTMLElement | DocumentFragment) => {
|
||||
const scripts = element.querySelectorAll(
|
||||
'script:not([src])',
|
||||
) as NodeListOf<HTMLScriptElement>;
|
||||
scripts.forEach((script) => {
|
||||
if (!script.type || script.type === 'application/javascript') {
|
||||
const newScript = document.createElement('script');
|
||||
Array.from(script.attributes).forEach((key) =>
|
||||
newScript.setAttribute(key.nodeName, key.nodeValue || ''),
|
||||
);
|
||||
newScript.text = script.text;
|
||||
script.replaceWith(newScript);
|
||||
}
|
||||
});
|
||||
const selector = 'script:not([src])';
|
||||
if (element instanceof HTMLElement && element.matches(selector)) {
|
||||
runScript(element as HTMLScriptElement);
|
||||
} else {
|
||||
const scripts = element.querySelectorAll(
|
||||
selector,
|
||||
) as NodeListOf<HTMLScriptElement>;
|
||||
scripts.forEach(runScript);
|
||||
}
|
||||
};
|
||||
|
||||
export { runInlineScripts };
|
||||
|
|
|
@ -351,7 +351,7 @@ In the new approach, we no longer need to attach an inline script but instead us
|
|||
|
||||
### Removal of jQuery from base client-side Widget and BoundWidget classes
|
||||
|
||||
The JavaScript base classes `Widget` and `BoundWidget` that provide client-side access to form widgets (see [](streamfield_widget_api)) no longer use jQuery. The `element` argument passed to the `BoundWidget` constructor, and the `input` property of `BoundWidget`, are now native DOM elements rather than jQuery collections. User code that extends these classes should be updated accordingly.
|
||||
The JavaScript base classes `Widget` and `BoundWidget` that provide client-side access to form widgets (see [](streamfield_widget_api)) no longer use jQuery. The `input` property of `BoundWidget` (previously a jQuery collection) is now a native DOM element, and the `element` argument passed to the `BoundWidget` constructor (previously a jQuery collection) is now passed as a native DOM element if the HTML representation consists of a single element, and an iterable of elements (`NodeList` or array) otherwise. User code that extends these classes should be updated accordingly.
|
||||
|
||||
### `window.URLify` deprecated
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue