Use lit-html to render form fields
This commit is contained in:
parent
62dbb1062f
commit
da131715ba
@ -1614,12 +1614,9 @@ describe("Groupchats", function () {
|
||||
.c('value').t('cauldronburn');
|
||||
_converse.connection._dataRecv(mock.createRequest(config_stanza));
|
||||
|
||||
const form = await u.waitUntil(() => view.el.querySelector('.muc-config-form'));
|
||||
expect(form.querySelectorAll('fieldset').length).toBe(2);
|
||||
const membersonly = view.el.querySelectorAll('input[name="muc#roomconfig_membersonly"]');
|
||||
expect(membersonly.length).toBe(1);
|
||||
expect(membersonly[0].getAttribute('type')).toBe('checkbox');
|
||||
membersonly[0].checked = true;
|
||||
const membersonly = await u.waitUntil(() => view.el.querySelector('input[name="muc#roomconfig_membersonly"]'));
|
||||
expect(membersonly.getAttribute('type')).toBe('checkbox');
|
||||
membersonly.checked = true;
|
||||
|
||||
const moderated = view.el.querySelectorAll('input[name="muc#roomconfig_moderatedroom"]');
|
||||
expect(moderated.length).toBe(1);
|
||||
|
@ -353,7 +353,7 @@ describe("The Registration Panel", function () {
|
||||
</iq>`);
|
||||
_converse.connection._dataRecv(mock.createRequest(stanza));
|
||||
expect(registerview.form_type).toBe('xform');
|
||||
expect(registerview.el.querySelectorAll('#converse-register input[required="required"]').length).toBe(3);
|
||||
expect(registerview.el.querySelectorAll('#converse-register input[required]').length).toBe(3);
|
||||
// Hide the controlbox so that we can see whether the test
|
||||
// passed or failed
|
||||
u.addClass('hidden', _converse.chatboxviews.get('controlbox').el);
|
||||
|
@ -4,7 +4,6 @@ import { CustomElement } from './element.js';
|
||||
import { __ } from '../i18n';
|
||||
import { api, converse } from "@converse/headless/core";
|
||||
import { html } from "lit-html";
|
||||
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
||||
|
||||
const { Strophe, $iq, sizzle } = converse.env;
|
||||
const u = converse.env.utils;
|
||||
@ -21,8 +20,7 @@ const tpl_command_form = (o, command) => {
|
||||
<input type="hidden" name="command_jid" value="${command.jid}"/>
|
||||
|
||||
<p class="form-help">${command.instructions}</p>
|
||||
<!-- Fields are generated internally, with xForm2webForm -->
|
||||
${ command.fields.map(field => unsafeHTML(field)) }
|
||||
${ command.fields }
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<input type="submit" class="btn btn-primary" value="${i18n_run}">
|
||||
|
@ -57,7 +57,7 @@ export default BootstrapModal.extend({
|
||||
command.fields;
|
||||
try {
|
||||
const iq = await api.sendIQ(stanza);
|
||||
command.fields = sizzle('field', iq).map(f => u.xForm2webForm(f, iq))
|
||||
command.fields = sizzle('field', iq).map(f => u.xForm2TemplateResult(f, iq))
|
||||
} catch (e) {
|
||||
if (e === null) {
|
||||
log.error(`Error: timeout while trying to execute command for ${jid}`);
|
||||
@ -83,7 +83,5 @@ export default BootstrapModal.extend({
|
||||
</command>
|
||||
</iq>
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
@ -32,7 +32,7 @@ const MUCConfigForm = View.extend({
|
||||
};
|
||||
return tpl_muc_config_form({
|
||||
'closeConfigForm': ev => this.closeConfigForm(ev),
|
||||
'fields': fields.map(f => u.xForm2webForm(f, stanza, options)),
|
||||
'fields': fields.map(f => u.xForm2TemplateResult(f, stanza, options)),
|
||||
'instructions': stanza.querySelector('instructions')?.textContent,
|
||||
'submitConfigForm': ev => this.submitConfigForm(ev),
|
||||
'title': stanza.querySelector('title')?.textContent
|
||||
|
@ -8,15 +8,16 @@
|
||||
*/
|
||||
import "./controlbox/index.js";
|
||||
import log from "@converse/headless/log";
|
||||
import tpl_form_input from "../templates/form_input.html";
|
||||
import tpl_form_username from "../templates/form_username.html";
|
||||
import tpl_form_input from "../templates/form_input.js";
|
||||
import tpl_form_url from "../templates/form_url.js";
|
||||
import tpl_form_username from "../templates/form_username.js";
|
||||
import tpl_register_panel from "../templates/register_panel.html";
|
||||
import tpl_registration_form from "../templates/registration_form.html";
|
||||
import tpl_registration_form from "../templates/registration_form.js";
|
||||
import tpl_registration_request from "../templates/registration_request.html";
|
||||
import tpl_spinner from "../templates/spinner.js";
|
||||
import utils from "@converse/headless/utils/form";
|
||||
import { View } from "@converse/skeletor/src/view";
|
||||
import { __ } from '../i18n';
|
||||
import { __ } from 'i18n';
|
||||
import { _converse, api, converse } from "@converse/headless/core";
|
||||
import { pick } from "lodash-es";
|
||||
import { render } from 'lit-html';
|
||||
@ -403,24 +404,19 @@ converse.plugins.add('converse-register', {
|
||||
}
|
||||
},
|
||||
|
||||
renderLegacyRegistrationForm (form) {
|
||||
Object.keys(this.fields).forEach(key => {
|
||||
getLegacyFormFields () {
|
||||
const input_fields = Object.keys(this.fields).map(key => {
|
||||
if (key === "username") {
|
||||
form.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
tpl_form_username({
|
||||
return tpl_form_username({
|
||||
'domain': ` @${this.domain}`,
|
||||
'name': key,
|
||||
'type': "text",
|
||||
'label': key,
|
||||
'value': '',
|
||||
'required': true
|
||||
})
|
||||
);
|
||||
});
|
||||
} else {
|
||||
form.insertAdjacentHTML(
|
||||
'beforeend',
|
||||
tpl_form_input({
|
||||
return tpl_form_input({
|
||||
'label': key,
|
||||
'name': key,
|
||||
'placeholder': key,
|
||||
@ -428,14 +424,20 @@ converse.plugins.add('converse-register', {
|
||||
'type': (key === 'password' || key === 'email') ? key : "text",
|
||||
'value': ''
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
// Show urls
|
||||
this.urls.forEach(u => form.insertAdjacentHTML(
|
||||
'afterend',
|
||||
'<a target="blank" rel="noopener" href="'+u+'">'+u+'</a>'
|
||||
));
|
||||
const urls = this.urls.map(u => tpl_form_url({'label': '', 'value': u}));
|
||||
return [...input_fields, ...urls];
|
||||
},
|
||||
|
||||
getFormFields (stanza) {
|
||||
if (this.form_type === 'xform') {
|
||||
return Array.from(stanza.querySelectorAll('field')).map(field =>
|
||||
utils.xForm2TemplateResult(field, stanza, {'domain': this.domain})
|
||||
);
|
||||
} else {
|
||||
return this.getLegacyFormFields();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -447,28 +449,14 @@ converse.plugins.add('converse-register', {
|
||||
*/
|
||||
renderRegistrationForm (stanza) {
|
||||
const form = this.el.querySelector('form');
|
||||
form.innerHTML = tpl_registration_form({
|
||||
'__': __,
|
||||
const tpl = tpl_registration_form({
|
||||
'domain': this.domain,
|
||||
'title': this.title,
|
||||
'instructions': this.instructions,
|
||||
'registration_domain': api.settings.get('registration_domain')
|
||||
'fields': this.fields,
|
||||
'form_fields': this.getFormFields(stanza)
|
||||
});
|
||||
|
||||
const buttons = form.querySelector('fieldset.buttons');
|
||||
if (this.form_type === 'xform') {
|
||||
stanza.querySelectorAll('field').forEach(field => {
|
||||
buttons.insertAdjacentHTML(
|
||||
'beforebegin',
|
||||
utils.xForm2webForm(field, stanza, {'domain': this.domain})
|
||||
);
|
||||
});
|
||||
} else {
|
||||
this.renderLegacyRegistrationForm(form);
|
||||
}
|
||||
if (!this.fields) {
|
||||
form.querySelector('.button-primary').classList.add('hidden');
|
||||
}
|
||||
render(tpl, form);
|
||||
form.classList.remove('hidden');
|
||||
this.model.set('registration_form_rendered', true);
|
||||
},
|
||||
|
@ -1,9 +0,0 @@
|
||||
{[ if (o.label) { ]}
|
||||
<label>
|
||||
{{{o.label}}}
|
||||
</label>
|
||||
{[ } ]}
|
||||
<img src="data:{{{o.type}}};base64,{{{o.data}}}">
|
||||
<input name="{{{o.name}}}" type="text" {[ if (o.required) { ]} required="required" {[ } ]} />
|
||||
|
||||
|
9
src/templates/form_captcha.js
Normal file
9
src/templates/form_captcha.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { html } from "lit-html";
|
||||
|
||||
export default (o) => html`
|
||||
<fieldset class="form-group">
|
||||
${o.label ? html`<label>${o.label}</label>` : '' }
|
||||
<img src="data:${o.type};base64,${o.data}">
|
||||
<input name="${o.name}" type="text" ?required="${o.required}" />
|
||||
</fieldset>
|
||||
`;
|
@ -1,4 +0,0 @@
|
||||
<div class="form-group">
|
||||
<input id="{{{o.id}}}" name="{{{o.name}}}" type="checkbox" {{{o.checked}}} {[ if (o.required) { ]} required {[ } ]} />
|
||||
<label class="form-check-label" for="{{{o.id}}}">{{{o.label}}}</label>
|
||||
</div>
|
7
src/templates/form_checkbox.js
Normal file
7
src/templates/form_checkbox.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { html } from "lit-html";
|
||||
|
||||
export default (o) => html`
|
||||
<fieldset class="form-group">
|
||||
<input id="${o.id}" name="${o.name}" type="checkbox" ?checked=${o.checked} ?required=${o.required} />
|
||||
<label class="form-check-label" for="${o.id}">${o.label}</label>
|
||||
</fieldset>`;
|
3
src/templates/form_help.js
Normal file
3
src/templates/form_help.js
Normal file
@ -0,0 +1,3 @@
|
||||
import { html } from "lit-html";
|
||||
|
||||
export default (o) => html`<p class="form-help">${o.text}</p>`;
|
@ -1,16 +0,0 @@
|
||||
<div class="form-group">
|
||||
{[ if (o.type !== 'hidden') { ]}
|
||||
<label for="{{{o.id}}}">{{{o.label}}}</label>
|
||||
{[ } ]}
|
||||
{[ if (o.type === 'password' && o.fixed_username) { ]}
|
||||
<!-- This is a hack to prevent Chrome from auto-filling the username in
|
||||
any of the other input fields in the MUC configuration form. -->
|
||||
<input class="hidden-username" type="text" autocomplete="username" value="{{{o.fixed_username}}}"></input>
|
||||
{[ } ]}
|
||||
<input
|
||||
class="form-control" name="{{{o.name}}}" type="{{{o.type}}}" id="{{{o.id}}}"
|
||||
{[ if (o.autocomplete) { ]} autocomplete="{{{o.autocomplete}}}" {[ } ]}
|
||||
{[ if (o.placeholder) { ]} placeholder="{{{o.placeholder}}}" {[ } ]}
|
||||
{[ if (o.value) { ]} value="{{{o.value}}}" {[ } ]}
|
||||
{[ if (o.required) { ]} required="required" {[ } ]} />
|
||||
</div>
|
22
src/templates/form_input.js
Normal file
22
src/templates/form_input.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { html } from "lit-html";
|
||||
|
||||
export default (o) => html`
|
||||
<div class="form-group">
|
||||
${ o.type !== 'hidden' ? html`<label for="${o.id}">${o.label}</label>` : '' }
|
||||
|
||||
<!-- This is a hack to prevent Chrome from auto-filling the username in
|
||||
any of the other input fields in the MUC configuration form. -->
|
||||
${ (o.type === 'password' && o.fixed_username) ? html`
|
||||
<input class="hidden-username" type="text" autocomplete="username" value="${o.fixed_username}"></input>
|
||||
` : '' }
|
||||
|
||||
<input
|
||||
autocomplete="${o.autocomplete || ''}"
|
||||
class="form-control"
|
||||
id="${o.id}"
|
||||
name="${o.name}"
|
||||
placeholder="${o.placeholder || ''}"
|
||||
type="${o.type}"
|
||||
value="${o.value || ''}"
|
||||
?required=${o.required} />
|
||||
</div>`;
|
@ -1,4 +0,0 @@
|
||||
<div class="form-group">
|
||||
<label for="{{{o.id}}}">{{{o.label}}}</label>
|
||||
<select class="form-control" id="{{{o.id}}}" name="{{{o.name}}}" {[ if (o.multiple) { ]} multiple="multiple" {[ } ]}>{{o.options}}</select>
|
||||
</div>
|
11
src/templates/form_select.js
Normal file
11
src/templates/form_select.js
Normal file
@ -0,0 +1,11 @@
|
||||
import { html } from "lit-html";
|
||||
|
||||
const tpl_option = (o) => html`<option value="${o.value}" ?selected="${o.selected}">${o.label}</option>`;
|
||||
|
||||
export default (o) => html`
|
||||
<div class="form-group">
|
||||
<label for="${o.id}">${o.label}</label>
|
||||
<select class="form-control" id="${o.id}" name="${o.name}" ?multiple="${o.multiple}">
|
||||
${o.options?.map(o => tpl_option(o))}
|
||||
</select>
|
||||
</div>`;
|
@ -1,2 +0,0 @@
|
||||
<label class="label-ta">{{{o.label}}}</label>
|
||||
<textarea name="{{{o.name}}}">{{{o.value}}}</textarea>
|
6
src/templates/form_textarea.js
Normal file
6
src/templates/form_textarea.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { html } from "lit-html";
|
||||
|
||||
export default (o) => html`
|
||||
<label class="label-ta">${o.label}</label>
|
||||
<textarea name="${o.name}">${o.value}</textarea>
|
||||
`;
|
@ -1,4 +0,0 @@
|
||||
<label>
|
||||
{{{o.label}}}
|
||||
<a class="form-url" target="_blank" rel="noopener" href="{{{o.value}}}">{{{o.value}}}</a>
|
||||
</label>
|
6
src/templates/form_url.js
Normal file
6
src/templates/form_url.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { html } from "lit-html";
|
||||
|
||||
export default (o) => html`
|
||||
<label>${o.label}
|
||||
<a class="form-url" target="_blank" rel="noopener" href="${o.value}">${o.value}</a>
|
||||
</label>`;
|
@ -1,15 +0,0 @@
|
||||
<div class="form-group">
|
||||
{[ if (o.label) { ]}
|
||||
<label>
|
||||
{{{o.label}}}
|
||||
</label>
|
||||
{[ } ]}
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<input name="{{{o.name}}}" type="{{{o.type}}}"
|
||||
{[ if (o.value) { ]} value="{{{o.value}}}" {[ } ]}
|
||||
{[ if (o.required) { ]} required="required" {[ } ]} />
|
||||
<div class="input-group-text col" title="{{{o.domain}}}">{{{o.domain}}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
15
src/templates/form_username.js
Normal file
15
src/templates/form_username.js
Normal file
@ -0,0 +1,15 @@
|
||||
import { html } from "lit-html";
|
||||
|
||||
export default (o) => html`
|
||||
<div class="form-group">
|
||||
${ o.label ? html`<label>${o.label}</label>` : '' }
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<input name="${o.name}"
|
||||
type="${o.type}"
|
||||
value="${o.value || ''}"
|
||||
?required="${o.required}" />
|
||||
<div class="input-group-text col" title="${o.domain}">${o.domain}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
@ -1,6 +1,5 @@
|
||||
import { html } from "lit-html";
|
||||
import { __ } from '../i18n';
|
||||
import { unsafeHTML } from 'lit-html/directives/unsafe-html.js';
|
||||
import { __ } from 'i18n';
|
||||
|
||||
export default (o) => {
|
||||
const i18n_save = __('Save');
|
||||
@ -10,8 +9,7 @@ export default (o) => {
|
||||
<fieldset class="form-group">
|
||||
<legend>${o.title}</legend>
|
||||
${ (o.title !== o.instructions) ? html`<p class="form-help">${o.instructions}</p>` : '' }
|
||||
<!-- Fields are generated internally, with xForm2webForm -->
|
||||
${ o.fields.map(field => unsafeHTML(field)) }
|
||||
${ o.fields }
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<input type="submit" class="btn btn-primary" value="${i18n_save}">
|
||||
|
@ -1,15 +0,0 @@
|
||||
<legend class="col-form-label">{{{o.__("Account Registration:")}}} {{{o.domain}}}</legend>
|
||||
<p class="title">{{{o.title}}}</p>
|
||||
<p class="form-help instructions">{{{o.instructions}}}</p>
|
||||
<div class="form-errors hidden"></div>
|
||||
|
||||
<fieldset class="buttons">
|
||||
<input type="submit" class="btn btn-primary" value="{{{o.__('Register')}}}"/>
|
||||
{[ if (!o.registration_domain) { ]}
|
||||
<input type="button" class="btn btn-secondary button-cancel" value="{{{o.__('Choose a different provider')}}}"/>
|
||||
{[ } ]}
|
||||
<div class="switch-form">
|
||||
<p>{{{ o.__("Already have a chat account?") }}}</p>
|
||||
<p><a class="login-here toggle-register-login" href="#converse/login">{{{o.__("Log in here")}}}</a></p>
|
||||
</div>
|
||||
</fieldset>
|
28
src/templates/registration_form.js
Normal file
28
src/templates/registration_form.js
Normal file
@ -0,0 +1,28 @@
|
||||
import { __ } from 'i18n';
|
||||
import { api } from "@converse/headless/core";
|
||||
import { html } from "lit-html";
|
||||
|
||||
export default (o) => {
|
||||
const i18n_choose_provider = __('Choose a different provider');
|
||||
const i18n_has_account = __("Already have a chat account?");
|
||||
const i18n_legend = __("Account Registration:");
|
||||
const i18n_login = __("Log in here");
|
||||
const i18n_register = __('Register');
|
||||
const registration_domain = api.settings.get('registration_domain')
|
||||
|
||||
return html`
|
||||
<legend class="col-form-label">${i18n_legend} ${o.domain}</legend>
|
||||
<p class="title">${o.title}</p>
|
||||
<p class="form-help instructions">${o.instructions}</p>
|
||||
<div class="form-errors hidden"></div>
|
||||
${ o.form_fields }
|
||||
|
||||
<fieldset class="buttons form-group">
|
||||
${ o.fields ? html`<input type="submit" class="btn btn-primary" value="${i18n_register}"/>` : '' }
|
||||
${ registration_domain ? '' : html`<input type="button" class="btn btn-secondary button-cancel" value="${i18n_choose_provider}"/>` }
|
||||
<div class="switch-form">
|
||||
<p>${i18n_has_account}</p>
|
||||
<p><a class="login-here toggle-register-login" href="#converse/login">${i18n_login}</a></p>
|
||||
</div>
|
||||
</fieldset>`;
|
||||
}
|
@ -1 +0,0 @@
|
||||
<option value="{{{o.value}}}" {[ if (o.selected) { ]} selected="selected" {[ } ]} >{{{o.label}}}</option>
|
@ -7,15 +7,15 @@ import URI from "urijs";
|
||||
import log from '@converse/headless/log';
|
||||
import tpl_audio from "../templates/audio.js";
|
||||
import tpl_file from "../templates/file.js";
|
||||
import tpl_form_captcha from "../templates/form_captcha.html";
|
||||
import tpl_form_checkbox from "../templates/form_checkbox.html";
|
||||
import tpl_form_input from "../templates/form_input.html";
|
||||
import tpl_form_select from "../templates/form_select.html";
|
||||
import tpl_form_textarea from "../templates/form_textarea.html";
|
||||
import tpl_form_url from "../templates/form_url.html";
|
||||
import tpl_form_username from "../templates/form_username.html";
|
||||
import tpl_form_captcha from "../templates/form_captcha.js";
|
||||
import tpl_form_checkbox from "../templates/form_checkbox.js";
|
||||
import tpl_form_help from "../templates/form_help.js";
|
||||
import tpl_form_input from "../templates/form_input.js";
|
||||
import tpl_form_select from "../templates/form_select.js";
|
||||
import tpl_form_textarea from "../templates/form_textarea.js";
|
||||
import tpl_form_url from "../templates/form_url.js";
|
||||
import tpl_form_username from "../templates/form_username.js";
|
||||
import tpl_image from "../templates/image.js";
|
||||
import tpl_select_option from "../templates/select_option.html";
|
||||
import tpl_video from "../templates/video.js";
|
||||
import u from "../headless/utils/core";
|
||||
import { api, converse } from "@converse/headless/core";
|
||||
@ -583,38 +583,38 @@ u.fadeIn = function (el, callback) {
|
||||
|
||||
|
||||
/**
|
||||
* Takes a field in XMPP XForm (XEP-004: Data Forms) format
|
||||
* and turns it into an HTML field.
|
||||
* Returns either text or a DOM element (which is not ideal, but fine for now).
|
||||
* @private
|
||||
* @method u#xForm2webForm
|
||||
* Takes an XML field in XMPP XForm (XEP-004: Data Forms) format returns a
|
||||
* [TemplateResult](https://lit-html.polymer-project.org/api/classes/_lit_html_.templateresult.html).
|
||||
* @method u#xForm2TemplateResult
|
||||
* @param { XMLElement } field - the field to convert
|
||||
* @param { XMLElement } stanza - the containing stanza
|
||||
* @param { Object } options
|
||||
* @returns { TemplateResult }
|
||||
*/
|
||||
u.xForm2webForm = function (field, stanza, options) {
|
||||
u.xForm2TemplateResult = function (field, stanza, options) {
|
||||
if (field.getAttribute('type') === 'list-single' ||
|
||||
field.getAttribute('type') === 'list-multi') {
|
||||
|
||||
const values = u.queryChildren(field, 'value').map(el => el?.textContent);
|
||||
const options = u.queryChildren(field, 'option').map(option => {
|
||||
const value = option.querySelector('value')?.textContent;
|
||||
return tpl_select_option({
|
||||
return {
|
||||
'value': value,
|
||||
'label': option.getAttribute('label'),
|
||||
'selected': values.includes(value),
|
||||
'required': !!field.querySelector('required')
|
||||
});
|
||||
};
|
||||
});
|
||||
return tpl_form_select({
|
||||
options,
|
||||
'id': u.getUniqueId(),
|
||||
'name': field.getAttribute('var'),
|
||||
'label': field.getAttribute('label'),
|
||||
'options': options.join(''),
|
||||
'multiple': (field.getAttribute('type') === 'list-multi'),
|
||||
'name': field.getAttribute('var'),
|
||||
'required': !!field.querySelector('required')
|
||||
});
|
||||
} else if (field.getAttribute('type') === 'fixed') {
|
||||
const text = field.querySelector('value')?.textContent;
|
||||
return '<p class="form-help">'+text+'</p>';
|
||||
return tpl_form_help({text});
|
||||
} else if (field.getAttribute('type') === 'jid-multi') {
|
||||
return tpl_form_textarea({
|
||||
'name': field.getAttribute('var'),
|
||||
@ -670,4 +670,5 @@ u.xForm2webForm = function (field, stanza, options) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default u;
|
||||
|
Loading…
Reference in New Issue
Block a user