feat(05-02): add search and note widgets with tests
- SearchWidget: provider dropdown, text input, button; opens search in new tab via window.open (D-14/D-15) - NoteWidget: MDEditor with compact toolbar, debounced autosave (1500ms), AbortController for in-flight cancellation (D-16/D-17/D-18) - rehype-sanitize enabled for Markdown XSS prevention (T-05-05) - Widget registry updated with wireSearchWidget/wireNoteWidget (no more placeholders) - dashboard-api.ts: added fetchSearchProviders, addSearchProvider, removeSearchProvider, signal support on updateWidgetConfig - 9 new tests passing (search: 5, note: 4) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -170,7 +170,7 @@ export const WIDGET_REGISTRY: Record<WidgetType, WidgetDefinition> = {
|
||||
descriptionKey: 'search.description',
|
||||
icon: SearchIcon,
|
||||
...WIDGET_CONSTRAINTS.search,
|
||||
component: PlaceholderWidget,
|
||||
component: PlaceholderWidget, // Replaced via wireSearchWidget()
|
||||
},
|
||||
calendar: {
|
||||
type: 'calendar',
|
||||
@@ -186,12 +186,12 @@ export const WIDGET_REGISTRY: Record<WidgetType, WidgetDefinition> = {
|
||||
descriptionKey: 'note.description',
|
||||
icon: NoteIcon,
|
||||
...WIDGET_CONSTRAINTS.note,
|
||||
component: PlaceholderWidget,
|
||||
component: PlaceholderWidget, // Replaced via wireNoteWidget()
|
||||
},
|
||||
};
|
||||
|
||||
// Wire actual ClockWidget lazily to avoid circular deps
|
||||
// (import is deferred so widget-registry can be imported by tests without
|
||||
// Wire actual widget components lazily to avoid circular deps
|
||||
// (imports are deferred so widget-registry can be imported by tests without
|
||||
// pulling in the entire React tree)
|
||||
let clockWired = false;
|
||||
export function wireClockWidget(
|
||||
@@ -202,3 +202,23 @@ export function wireClockWidget(
|
||||
clockWired = true;
|
||||
}
|
||||
}
|
||||
|
||||
let searchWired = false;
|
||||
export function wireSearchWidget(
|
||||
component: ComponentType<WidgetProps>,
|
||||
) {
|
||||
if (!searchWired) {
|
||||
WIDGET_REGISTRY.search.component = component;
|
||||
searchWired = true;
|
||||
}
|
||||
}
|
||||
|
||||
let noteWired = false;
|
||||
export function wireNoteWidget(
|
||||
component: ComponentType<WidgetProps>,
|
||||
) {
|
||||
if (!noteWired) {
|
||||
WIDGET_REGISTRY.note.component = component;
|
||||
noteWired = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user