file manager: scaffold the tree filter (multiselect)
Add a Filter multiselect next to the sort pills in the file tree toolbar. The option list is large and undecided, so FS_FILTER_OPTIONS is empty and the per-node match is a no-op for now; the mechanism is wired end to end: - FS_FILTER_OPTIONS const (empty), pysimFsFilter state persisted to simple_fs_filter, pysimFsSetFilter / pysimFsFilterMatch / pysimFsFilterRender, and the render loop consults pysimFsFilterMatch. - The dropdown re-renders on language switch; "Filter:" and "(no filters yet)" are translated (EN/RU). Frontend 1132 tests green (6 new scaffold pins).
This commit is contained in:
1 parent
105efe62da
commit
3878156646
3 files changed
+112
No files matched your search
@@ -0,0 +1,56 @@
|
||||
const { test } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const html = fs.readFileSync(path.join(__dirname, '..', 'index.html'), 'utf8');
|
||||
|
||||
function extractFunc(src, name) {
|
||||
const re = new RegExp('function\\s+' + name + '\\s*\\([^)]*\\)\\s*\\{');
|
||||
const m = re.exec(src);
|
||||
if (!m) throw new Error('function ' + name + ' not found');
|
||||
let i = m.index + m[0].length - 1;
|
||||
let depth = 0;
|
||||
for (; i < src.length; i++) {
|
||||
if (src[i] === '{') depth++;
|
||||
else if (src[i] === '}') {
|
||||
depth--;
|
||||
if (depth === 0) break;
|
||||
}
|
||||
}
|
||||
return src.slice(m.index, i + 1);
|
||||
}
|
||||
|
||||
// module state the extracted match function closes over
|
||||
let pysimFsFilter = new Set();
|
||||
eval(extractFunc(html, 'pysimFsFilterMatch'));
|
||||
|
||||
test('the file tree has a multiselect filter next to the sort pills', () => {
|
||||
assert.match(html, /id="pysim-fs-filter"[^>]*multiple[^>]*onchange="pysimFsSetFilter\(\)"/);
|
||||
assert.match(html, /data-l10n="Filter:"/);
|
||||
});
|
||||
|
||||
test('the filter options list is scaffolded and empty for now', () => {
|
||||
assert.match(html, /const FS_FILTER_OPTIONS = \[/);
|
||||
});
|
||||
|
||||
test('the render consults the filter when walking children', () => {
|
||||
assert.match(html, /pysimFsFilterMatch\(child\)/);
|
||||
});
|
||||
|
||||
test('the filter state is persisted under simple_fs_filter', () => {
|
||||
const fn = extractFunc(html, 'pysimFsSetFilter');
|
||||
assert.ok(fn.indexOf("localStorage.setItem('simple_fs_filter'") > 0, fn);
|
||||
assert.ok(fn.indexOf('pysimFsRenderTree()') > 0, fn);
|
||||
});
|
||||
|
||||
test('the dropdown is re-rendered on language switch', () => {
|
||||
const fn = extractFunc(html, 'refreshDynamicI18n');
|
||||
assert.ok(fn.indexOf('pysimFsFilterRender()') > 0, fn);
|
||||
});
|
||||
|
||||
test('an empty filter matches everything (scaffold no-op until criteria land)', () => {
|
||||
pysimFsFilter = new Set();
|
||||
assert.strictEqual(pysimFsFilterMatch({ name: 'EF.ADN', fid: '6F3A' }), true);
|
||||
assert.strictEqual(pysimFsFilterMatch({ name: 'DF.GSM', fid: '7F20', isDir: true }), true);
|
||||
});
|
||||
@@ -22,8 +22,10 @@ function extractFunc(src, name) {
|
||||
}
|
||||
|
||||
let code = 'var pysimFsSort = "fid";\n';
|
||||
code += 'var pysimFsFilter = new Set();\n';
|
||||
code += extractFunc(html, 'pysimFsSortChildren') + '\n';
|
||||
code += extractFunc(html, 'pysimFsNodePath') + '\n';
|
||||
code += extractFunc(html, 'pysimFsFilterMatch') + '\n';
|
||||
code += extractFunc(html, 'pysimFsRenderNode') + '\n';
|
||||
code += 'globalThis.esc = s => s;\nglobalThis.t = s => s;\n';
|
||||
eval(code);
|
||||
|
||||
Reference in new issue
Block a user