mirror of
https://github.com/wagtail/wagtail.git
synced 2024-11-25 05:02:57 +01:00
Eliminate jQuery from telepath widget classes
This commit is contained in:
parent
6d1fe98f6a
commit
9b74c83195
@ -1,7 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`telepath: wagtail.widgets.CheckboxInput it renders correctly 1`] = `"<input type="checkbox" name="sugar" id="id-sugar" checked="checked">"`;
|
||||
|
||||
exports[`telepath: wagtail.widgets.RadioSelect it renders correctly 1`] = `
|
||||
"<ul id="the-id">
|
||||
<li>
|
||||
|
@ -1,4 +1,3 @@
|
||||
/* global $ */
|
||||
import { gettext } from '../../../utils/gettext';
|
||||
|
||||
class BoundWidget {
|
||||
@ -10,8 +9,11 @@ class BoundWidget {
|
||||
parentCapabilities,
|
||||
options,
|
||||
) {
|
||||
var selector = ':input[name="' + name + '"]';
|
||||
this.input = element.find(selector).addBack(selector); // find, including element itself
|
||||
var selector = ':is(input,select,textarea,button)[name="' + name + '"]';
|
||||
// find, including element itself
|
||||
this.input = element.matches(selector)
|
||||
? element
|
||||
: element.querySelector(selector);
|
||||
this.idForLabel = idForLabel;
|
||||
this.setState(initialState);
|
||||
this.parentCapabilities = parentCapabilities || new Map();
|
||||
@ -19,15 +21,15 @@ class BoundWidget {
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.input.val();
|
||||
return this.input.value;
|
||||
}
|
||||
|
||||
getState() {
|
||||
return this.input.val();
|
||||
return this.input.value;
|
||||
}
|
||||
|
||||
setState(state) {
|
||||
this.input.val(state);
|
||||
this.input.value = state;
|
||||
}
|
||||
|
||||
getTextLabel(opts) {
|
||||
@ -65,17 +67,31 @@ class Widget {
|
||||
parentCapabilities,
|
||||
options = {},
|
||||
) {
|
||||
var html = this.html.replace(/__NAME__/g, name).replace(/__ID__/g, id);
|
||||
var idForLabel = this.idPattern.replace(/__ID__/g, id);
|
||||
var dom = $(html);
|
||||
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 */
|
||||
const tempContainer = document.createElement('div');
|
||||
tempContainer.innerHTML = html.trim();
|
||||
const dom = tempContainer.firstChild;
|
||||
|
||||
/* replace the placeholder with the new element */
|
||||
placeholder.replaceWith(dom);
|
||||
|
||||
/* execute any scripts in the new element */
|
||||
dom.querySelectorAll('script').forEach((script) => {
|
||||
const newScript = document.createElement('script');
|
||||
newScript.text = script.text;
|
||||
script.replaceWith(newScript);
|
||||
});
|
||||
|
||||
// Add any extra attributes we received to the HTML of the widget
|
||||
if (typeof options?.attributes === 'object') {
|
||||
Object.entries(options.attributes).forEach(([key, value]) => {
|
||||
dom.attr(key, value);
|
||||
dom.setAttribute(key, value);
|
||||
});
|
||||
}
|
||||
$(placeholder).replaceWith(dom);
|
||||
|
||||
// eslint-disable-next-line new-cap
|
||||
return new this.boundWidgetClass(
|
||||
dom,
|
||||
@ -91,16 +107,15 @@ window.telepath.register('wagtail.widgets.Widget', Widget);
|
||||
|
||||
class BoundCheckboxInput extends BoundWidget {
|
||||
getValue() {
|
||||
return this.input.is(':checked');
|
||||
return this.input.checked;
|
||||
}
|
||||
|
||||
getState() {
|
||||
return this.input.is(':checked');
|
||||
return this.input.checked;
|
||||
}
|
||||
|
||||
setState(state) {
|
||||
// if false, set attribute value to null to remove it
|
||||
this.input.attr('checked', state || null);
|
||||
this.input.checked = state;
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,19 +134,24 @@ class BoundRadioSelect {
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.element.find(this.selector).val();
|
||||
return this.element.querySelector(this.selector)?.value;
|
||||
}
|
||||
|
||||
getState() {
|
||||
return this.element.find(this.selector).val();
|
||||
return this.element.querySelector(this.selector)?.value;
|
||||
}
|
||||
|
||||
setState(state) {
|
||||
this.element.find('input[name="' + this.name + '"]').val([state]);
|
||||
const inputs = this.element.querySelectorAll(
|
||||
'input[name="' + this.name + '"]',
|
||||
);
|
||||
for (let i = 0; i < inputs.length; i += 1) {
|
||||
inputs[i].checked = inputs[i].value === state;
|
||||
}
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.element.find('input[name="' + this.name + '"]').focus();
|
||||
this.element.querySelector('input[name="' + this.name + '"]')?.focus();
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,7 +162,8 @@ window.telepath.register('wagtail.widgets.RadioSelect', RadioSelect);
|
||||
|
||||
class BoundSelect extends BoundWidget {
|
||||
getTextLabel() {
|
||||
return this.input.find(':selected').text();
|
||||
const selectedOption = this.input.selectedOptions[0];
|
||||
return selectedOption ? selectedOption.text : '';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,10 +46,14 @@ describe('telepath: wagtail.widgets.Widget', () => {
|
||||
|
||||
test('getValue() returns the current value', () => {
|
||||
expect(boundWidget.getValue()).toBe('The Value');
|
||||
document.querySelector('input').value = 'New Value';
|
||||
expect(boundWidget.getValue()).toBe('New Value');
|
||||
});
|
||||
|
||||
test('getState() returns the current state', () => {
|
||||
expect(boundWidget.getState()).toBe('The Value');
|
||||
document.querySelector('input').value = 'New Value';
|
||||
expect(boundWidget.getState()).toBe('New Value');
|
||||
});
|
||||
|
||||
test('setState() changes the current state', () => {
|
||||
@ -87,6 +91,37 @@ describe('telepath: wagtail.widgets.Widget', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('telepath: wagtail.widgets.Widget with inline JS', () => {
|
||||
let boundWidget;
|
||||
let widgetDef;
|
||||
|
||||
beforeEach(() => {
|
||||
// Create a placeholder to render the widget
|
||||
document.body.innerHTML = '<div id="placeholder"></div>';
|
||||
|
||||
widgetDef = window.telepath.unpack({
|
||||
_type: 'wagtail.widgets.Widget',
|
||||
_args: [
|
||||
'<div><input type="text" name="__NAME__" maxlength="255" id="__ID__"><script>document.getElementById("__ID__").className = "custom-class";</script></div>',
|
||||
'__ID__',
|
||||
],
|
||||
});
|
||||
boundWidget = widgetDef.render(
|
||||
document.getElementById('placeholder'),
|
||||
'the-name',
|
||||
'the-id',
|
||||
'The Value',
|
||||
);
|
||||
});
|
||||
|
||||
test('it renders correctly', () => {
|
||||
expect(document.body.querySelector('input').outerHTML).toBe(
|
||||
'<input type="text" name="the-name" maxlength="255" id="the-id" class="custom-class">',
|
||||
);
|
||||
expect(document.querySelector('input').value).toBe('The Value');
|
||||
});
|
||||
});
|
||||
|
||||
describe('telepath: wagtail.widgets.RadioSelect', () => {
|
||||
let boundWidget;
|
||||
|
||||
@ -141,9 +176,8 @@ describe('telepath: wagtail.widgets.RadioSelect', () => {
|
||||
test('focus() focuses the text input', () => {
|
||||
boundWidget.focus();
|
||||
|
||||
// Note: This widget always focuses the last element
|
||||
expect(document.activeElement).toBe(
|
||||
document.querySelector('input[value="coffee"]'),
|
||||
document.querySelector('input[value="tea"]'),
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -168,7 +202,6 @@ describe('telepath: wagtail.widgets.CheckboxInput', () => {
|
||||
});
|
||||
|
||||
test('it renders correctly', () => {
|
||||
expect(document.body.innerHTML).toMatchSnapshot();
|
||||
expect(document.querySelector('input[id="id-sugar"]').checked).toBe(true);
|
||||
});
|
||||
|
||||
|
@ -276,3 +276,7 @@ In the new approach, we no longer need to attach an inline script but instead us
|
||||
data-w-block-arguments-value="[{value_json},{error_json}]"
|
||||
></div>
|
||||
```
|
||||
|
||||
### 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.
|
||||
|
Loading…
Reference in New Issue
Block a user