109 lines
6.5 KiB
JavaScript
109 lines
6.5 KiB
JavaScript
// Generated, do not edit. sha256:1850735074515476989419d5951d7734c561fc372a890ebd08e743f8ba996291
|
|||
|
|
import { describe, it } from 'node:test';
|
||
|
|
import assert from 'node:assert/strict';
|
||
|
|
import { installDom } from '../helpers/mini-dom.mjs';
|
||
|
|
import { editInputHtml, editCellHtml, expandRowHtml, captureValues } from '../../client/ui/table-edit.js';
|
||
|
|
|
||
|
|
const dom = installDom();
|
||
|
|
const parse = html => { const el = dom.host(); el.innerHTML = html; return el; };
|
||
|
|
const convert = { label: 'Omrekenen', canConvert: a => !!(a && a.subunit), icon: '<i class="swap"></i>' };
|
||
|
|
|
||
|
|
describe('editInputHtml', () => {
|
||
|
|
it('a select marks the current value, compared as text', () => {
|
||
|
|
const el = parse(editInputHtml({ key: 'k', edit: { type: 'select' } }, { k: 2 }, { options: [{ value: '1', label: 'A' }, { value: '2', label: '<B>' }] }));
|
||
|
|
assert.equal(el.querySelector('select').value, '2');
|
||
|
|
assert.equal(el.querySelector('option[value="2"]').textContent, '<B>');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('a checkbox is checked by a truthy value; a textarea gets its class and no style', () => {
|
||
|
|
assert.equal(parse(editInputHtml({ key: 'c', edit: { type: 'checkbox' } }, { c: 1 })).querySelector('input').checked, true);
|
||
|
|
const html = editInputHtml({ key: 't', edit: { type: 'textarea' } }, { t: 'a<b' });
|
||
|
|
assert.doesNotMatch(html, /style=/);
|
||
|
|
assert.equal(parse(html).querySelector('textarea.tbl-textarea').value, 'a<b');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('the input type follows the edit type, and an unknown type is text', () => {
|
||
|
|
for (const [type, want] of [['number', 'number'], ['date', 'date'], ['time', 'time'], ['text', 'text'], [undefined, 'text']]) {
|
||
|
|
assert.equal(parse(editInputHtml({ key: 'x', edit: { type } }, {})).querySelector('input').type, want);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
it('placeholder as a function of the values, attrs escaped, value escaped, null as empty', () => {
|
||
|
|
const input = parse(editInputHtml({ key: 'x', edit: { placeholder: v => `voor ${v.kind}`, attrs: { 'data-x': 'a"b' } } }, { kind: 'k', x: null })).querySelector('input');
|
||
|
|
assert.equal(input.getAttribute('placeholder'), 'voor k');
|
||
|
|
assert.equal(input.getAttribute('data-x'), 'a"b');
|
||
|
|
assert.equal(input.value, '');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('the convert trigger only when the chosen asset can be converted, and only for a column that converts', () => {
|
||
|
|
const col = { key: 'amount', edit: { type: 'number', convert: v => v.asset } };
|
||
|
|
const yes = parse(editInputHtml(col, { asset: { subunit: 'sat' } }, { convert }));
|
||
|
|
assert.equal(yes.querySelector('[data-convert="amount"]').title, 'Omrekenen');
|
||
|
|
assert.ok(yes.querySelector('.conv-field i.swap'));
|
||
|
|
assert.equal(parse(editInputHtml(col, { asset: { subunit: '' } }, { convert })).querySelector('[data-convert]'), null);
|
||
|
|
assert.equal(parse(editInputHtml({ key: 'n', edit: { type: 'number' } }, {}, { convert })).querySelector('[data-convert]'), null);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('a custom control renders itself from the values', () => {
|
||
|
|
assert.equal(editInputHtml({ key: 'x', edit: { render: v => `<b data-field="x">${v.x}</b>` } }, { x: 5 }), '<b data-field="x">5</b>');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('editCellHtml', () => {
|
||
|
|
it('an empty cell for a hidden field and for a field in the drawer, without data-label', () => {
|
||
|
|
const hidden = parse(`<table><tbody><tr>${editCellHtml({ key: 'a', label: 'A', edit: { visible: () => false } }, {})}</tr></tbody></table>`).querySelector('td');
|
||
|
|
const drawer = parse(`<table><tbody><tr>${editCellHtml({ key: 'a', label: 'A', edit: { expand: true } }, {})}</tr></tbody></table>`).querySelector('td');
|
||
|
|
for (const td of [hidden, drawer]) { assert.equal(td.children.length, 0); assert.equal(td.dataset.label, undefined); }
|
||
|
|
const shown = parse(`<table><tbody><tr>${editCellHtml({ key: 'a', label: 'A', edit: {} }, { a: 1 })}</tr></tbody></table>`).querySelector('td');
|
||
|
|
assert.equal(shown.dataset.label, 'A');
|
||
|
|
assert.equal(shown.querySelector('[data-field="a"]').value, '1');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('expandRowHtml', () => {
|
||
|
|
const cols = [
|
||
|
|
{ key: 'note', label: 'Notitie', edit: { type: 'textarea', expand: true } },
|
||
|
|
{ key: 'ref', label: 'Ref', edit: { expand: true } },
|
||
|
|
{ key: 'gone', label: 'Weg', edit: { expand: true, visible: () => false } },
|
||
|
|
{ key: 'inline', label: 'In', edit: {} },
|
||
|
|
];
|
||
|
|
const optsFor = () => ({});
|
||
|
|
|
||
|
|
it('the visible drawer fields, a textarea full width, and the error escaped', () => {
|
||
|
|
const html = expandRowHtml({ editableCols: cols, vals: {}, error: '<bad>', colCount: 5, optsFor });
|
||
|
|
assert.doesNotMatch(html, /style=/);
|
||
|
|
const el = parse(`<table><tbody>${html}</tbody></table>`);
|
||
|
|
assert.equal(el.querySelector('td').getAttribute('colspan'), '5');
|
||
|
|
assert.deepEqual(el.querySelectorAll('.edit-expand-field').map(f => f.classList.contains('is-full')), [true, false]);
|
||
|
|
assert.equal(el.querySelector('[data-field="gone"]'), null);
|
||
|
|
assert.equal(el.querySelector('.edit-expand-error').textContent, '<bad>');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('nothing at all without drawer fields, an error or actions', () => {
|
||
|
|
assert.equal(expandRowHtml({ editableCols: [cols[3]], vals: {}, error: '', colCount: 2, optsFor }), '');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('the actions alone make a drawer, and they come after the fields and the error', () => {
|
||
|
|
const alone = parse(`<table><tbody>${expandRowHtml({ editableCols: [cols[3]], vals: {}, error: '', colCount: 2, optsFor, actions: '<div class="edit-actions"></div>' })}</tbody></table>`);
|
||
|
|
assert.ok(alone.querySelector('.edit-expand-row .edit-actions'));
|
||
|
|
assert.equal(alone.querySelector('.edit-expand-fields'), null, 'no empty fields block');
|
||
|
|
const all = parse(`<table><tbody>${expandRowHtml({ editableCols: cols, vals: {}, error: 'e', colCount: 5, optsFor, actions: '<div class="edit-actions"></div>' })}</tbody></table>`);
|
||
|
|
assert.deepEqual(all.querySelector('td').children.map(c => c.className.split(' ').pop()), ['edit-expand-fields', 'edit-expand-error', 'edit-actions']);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('captureValues', () => {
|
||
|
|
const cols = [
|
||
|
|
{ key: 'a', edit: {} },
|
||
|
|
{ key: 'on', edit: { type: 'checkbox' } },
|
||
|
|
{ key: 'hidden', edit: {} },
|
||
|
|
{ key: 'own', edit: { read: host => host.dataset.v + '!' } },
|
||
|
|
];
|
||
|
|
|
||
|
|
it('reads values, a checkbox as a boolean, a custom control through its read, and keeps a hidden field', () => {
|
||
|
|
const scope = parse('<input data-field="a" value="x"><input type="checkbox" data-field="on"><span data-field="own" data-v="z"></span>');
|
||
|
|
scope.querySelector('[data-field="on"]').checked = true;
|
||
|
|
const vals = captureValues(scope, cols, { hidden: 'kept' });
|
||
|
|
assert.deepEqual(vals, { a: 'x', on: true, hidden: 'kept', own: 'z!' });
|
||
|
|
});
|
||
|
|
});
|