ext(affordances): wireGroupAutocomplete via <datalist>
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { wireGroupAutocomplete } from '../group-autocomplete';
|
||||
|
||||
describe('wireGroupAutocomplete', () => {
|
||||
let form: HTMLElement;
|
||||
|
||||
beforeEach(() => {
|
||||
// Clean up any datalist from a prior test
|
||||
document.getElementById('groups-datalist')?.remove();
|
||||
form = document.createElement('div');
|
||||
form.innerHTML = `<input id="f-group" type="text" />`;
|
||||
document.body.appendChild(form);
|
||||
});
|
||||
|
||||
it('attaches datalist with all groups', async () => {
|
||||
const sendMessage = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
data: { groups: ['personal', 'work', 'finance'] },
|
||||
});
|
||||
await wireGroupAutocomplete(form, { sendMessage });
|
||||
const list = document.getElementById('groups-datalist') as HTMLDataListElement | null;
|
||||
expect(list).not.toBeNull();
|
||||
const opts = Array.from(list!.querySelectorAll('option')).map((o) => o.value);
|
||||
expect(opts).toEqual(['personal', 'work', 'finance']);
|
||||
const input = form.querySelector('#f-group') as HTMLInputElement;
|
||||
expect(input.getAttribute('list')).toBe('groups-datalist');
|
||||
});
|
||||
|
||||
it('is a no-op if SW returns error', async () => {
|
||||
const sendMessage = vi.fn().mockResolvedValue({ ok: false, error: 'vault_locked' });
|
||||
await wireGroupAutocomplete(form, { sendMessage });
|
||||
const input = form.querySelector('#f-group') as HTMLInputElement;
|
||||
expect(input.getAttribute('list')).toBeNull();
|
||||
});
|
||||
});
|
||||
23
extension/src/shared/form-affordances/group-autocomplete.ts
Normal file
23
extension/src/shared/form-affordances/group-autocomplete.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
export interface GroupAutocompleteOpts {
|
||||
sendMessage: (msg: { type: 'list_groups' }) => Promise<{ ok: boolean; data?: { groups: string[] }; error?: string }>;
|
||||
}
|
||||
|
||||
const DATALIST_ID = 'groups-datalist';
|
||||
|
||||
export async function wireGroupAutocomplete(form: HTMLElement, opts: GroupAutocompleteOpts): Promise<void> {
|
||||
const input = form.querySelector<HTMLInputElement>('#f-group');
|
||||
if (!input) return;
|
||||
const resp = await opts.sendMessage({ type: 'list_groups' });
|
||||
if (!resp.ok || !resp.data) return;
|
||||
|
||||
// Datalists must live in the document, not nested inside an input. Reuse if
|
||||
// we've already mounted one this session.
|
||||
let list = document.getElementById(DATALIST_ID) as HTMLDataListElement | null;
|
||||
if (!list) {
|
||||
list = document.createElement('datalist');
|
||||
list.id = DATALIST_ID;
|
||||
document.body.appendChild(list);
|
||||
}
|
||||
list.innerHTML = resp.data.groups.map((g) => `<option value="${g.replace(/"/g, '"')}"></option>`).join('');
|
||||
input.setAttribute('list', DATALIST_ID);
|
||||
}
|
||||
Reference in New Issue
Block a user