mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-07-27 16:49:51 -07:00
feat(notifications): add overlay notifications with position config (#110)
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
|
||||
import { renderControl } from './settings-controls';
|
||||
import type { ConfigSettingsField } from '../types/settings';
|
||||
|
||||
class FakeOption {
|
||||
value = '';
|
||||
textContent: string | null = null;
|
||||
selected = false;
|
||||
className = '';
|
||||
}
|
||||
|
||||
class FakeSelect {
|
||||
value = '';
|
||||
className = '';
|
||||
options: FakeOption[] = [];
|
||||
private readonly listeners = new Map<string, Array<() => void>>();
|
||||
|
||||
append(...children: FakeOption[]): void {
|
||||
for (const child of children) {
|
||||
this.options.push(child);
|
||||
if (child.selected) {
|
||||
this.value = child.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addEventListener(type: string, listener: () => void): void {
|
||||
const listeners = this.listeners.get(type) ?? [];
|
||||
listeners.push(listener);
|
||||
this.listeners.set(type, listeners);
|
||||
}
|
||||
|
||||
dispatchEvent(event: Event): boolean {
|
||||
for (const listener of this.listeners.get(event.type) ?? []) {
|
||||
listener();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function installDocumentStub(): () => void {
|
||||
const previousDocument = globalThis.document;
|
||||
globalThis.document = {
|
||||
createElement(tagName: string) {
|
||||
return tagName === 'select' ? new FakeSelect() : new FakeOption();
|
||||
},
|
||||
} as unknown as Document;
|
||||
return () => {
|
||||
globalThis.document = previousDocument;
|
||||
};
|
||||
}
|
||||
|
||||
function createSelectField(): ConfigSettingsField {
|
||||
return {
|
||||
id: 'updates.notificationType',
|
||||
label: 'Notification Type',
|
||||
description: 'How SubMiner announces available updates.',
|
||||
configPath: 'updates.notificationType',
|
||||
category: 'tracking-app',
|
||||
section: 'Updates',
|
||||
control: 'select',
|
||||
defaultValue: 'system',
|
||||
enumValues: ['overlay', 'system', 'both', 'none'],
|
||||
restartBehavior: 'restart',
|
||||
};
|
||||
}
|
||||
|
||||
test('select controls show config-only current values without offering them otherwise', () => {
|
||||
const restoreDocument = installDocumentStub();
|
||||
const updates: Array<{ path: string; value: unknown }> = [];
|
||||
try {
|
||||
const control = renderControl(createSelectField(), {
|
||||
valueForField: () => 'osd-system',
|
||||
valueForPath: () => undefined,
|
||||
updateDraft: (path, value) => updates.push({ path, value }),
|
||||
resetDraftPath: () => {},
|
||||
setFieldError: () => {},
|
||||
}) as HTMLSelectElement;
|
||||
|
||||
assert.equal(control.value, 'osd-system');
|
||||
assert.deepEqual(
|
||||
Array.from(control.options).map((option) => option.value),
|
||||
['osd-system', 'overlay', 'system', 'both', 'none'],
|
||||
);
|
||||
|
||||
control.value = 'overlay';
|
||||
control.dispatchEvent(new Event('change'));
|
||||
|
||||
assert.deepEqual(updates, [{ path: 'updates.notificationType', value: 'overlay' }]);
|
||||
} finally {
|
||||
restoreDocument();
|
||||
}
|
||||
});
|
||||
@@ -216,7 +216,15 @@ export function renderControl(
|
||||
|
||||
if (field.control === 'select') {
|
||||
const select = createElement('select', 'config-input') as HTMLSelectElement;
|
||||
for (const enumValue of field.enumValues ?? []) {
|
||||
const enumValues = field.enumValues ?? [];
|
||||
if (typeof value === 'string' && value.length > 0 && !enumValues.includes(value)) {
|
||||
const option = createElement('option') as HTMLOptionElement;
|
||||
option.value = value;
|
||||
option.textContent = `${value} (config file only)`;
|
||||
option.selected = true;
|
||||
select.append(option);
|
||||
}
|
||||
for (const enumValue of enumValues) {
|
||||
const option = createElement('option') as HTMLOptionElement;
|
||||
option.value = enumValue;
|
||||
option.textContent = enumValue;
|
||||
|
||||
Reference in New Issue
Block a user