diff --git a/client/src/entrypoints/admin/telepath/widgets.js b/client/src/entrypoints/admin/telepath/widgets.js index 4dfe7fa504ea..30c787c757f1 100644 --- a/client/src/entrypoints/admin/telepath/widgets.js +++ b/client/src/entrypoints/admin/telepath/widgets.js @@ -2,12 +2,20 @@ import { gettext } from '../../../utils/gettext'; class BoundWidget { - constructor(element, name, idForLabel, initialState, parentCapabilities) { + constructor( + element, + name, + idForLabel, + initialState, + parentCapabilities, + options, + ) { var selector = ':input[name="' + name + '"]'; this.input = element.find(selector).addBack(selector); // find, including element itself this.idForLabel = idForLabel; this.setState(initialState); this.parentCapabilities = parentCapabilities || new Map(); + this.options = options; } getValue() { @@ -49,10 +57,24 @@ class Widget { boundWidgetClass = BoundWidget; - render(placeholder, name, id, initialState, parentCapabilities) { + render( + placeholder, + name, + id, + initialState, + 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); + + // 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); + }); + } $(placeholder).replaceWith(dom); // eslint-disable-next-line new-cap return new this.boundWidgetClass( @@ -61,6 +83,7 @@ class Widget { idForLabel, initialState, parentCapabilities, + options, ); } } @@ -349,7 +372,7 @@ class DraftailRichTextArea { this.options = options; } - render(container, name, id, initialState, parentCapabilities) { + render(container, name, id, initialState, parentCapabilities, options = {}) { const input = document.createElement('input'); input.type = 'hidden'; input.id = id; @@ -363,7 +386,7 @@ class DraftailRichTextArea { const boundDraftail = new BoundDraftailWidget( input, - this.options, + { ...options, ...this.options }, parentCapabilities, ); diff --git a/client/src/entrypoints/admin/telepath/widgets.test.js b/client/src/entrypoints/admin/telepath/widgets.test.js index 6bff475eed20..065831a64854 100644 --- a/client/src/entrypoints/admin/telepath/widgets.test.js +++ b/client/src/entrypoints/admin/telepath/widgets.test.js @@ -16,12 +16,13 @@ window.comments = { describe('telepath: wagtail.widgets.Widget', () => { let boundWidget; + let widgetDef; beforeEach(() => { // Create a placeholder to render the widget document.body.innerHTML = '
'; - const widgetDef = window.telepath.unpack({ + widgetDef = window.telepath.unpack({ _type: 'wagtail.widgets.Widget', _args: [ '', @@ -60,6 +61,30 @@ describe('telepath: wagtail.widgets.Widget', () => { boundWidget.focus(); expect(document.activeElement).toBe(document.querySelector('input')); }); + + test('it should support options with attributes', () => { + document.body.innerHTML = '
'; + boundWidget = widgetDef.render( + document.getElementById('placeholder'), + 'the-name', + 'the-id', + 'The Value', + {}, + { + attributes: { + 'maxLength': 512, + 'aria-describedby': 'some-id', + 'required': '', + }, + }, + ); + + const input = document.querySelector('input'); + + expect(input.maxLength).toBe(512); + expect(input.getAttribute('aria-describedby')).toBe('some-id'); + expect(input.required).toBe(true); + }); }); describe('telepath: wagtail.widgets.RadioSelect', () => {