fix(quick-261005-blw): Links in Notiz-Kacheln oeffnen in neuem Tab
NoteLink ersetzt <a> der Markdown-Vorschau: target=_blank, rel=noopener noreferrer; #-Sprungmarken bleiben im Fenster. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ import userEvent from '@testing-library/user-event';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import MDEditor from '@uiw/react-md-editor';
|
||||
import rehypeSanitize from 'rehype-sanitize';
|
||||
import { NoteCheckbox, isTaskLine, toggleTaskLine } from './note-task-list';
|
||||
import { NoteCheckbox, NoteLink, isTaskLine, toggleTaskLine } from './note-task-list';
|
||||
|
||||
describe('note-task-list', () => {
|
||||
describe('isTaskLine', () => {
|
||||
@@ -143,5 +143,30 @@ describe('note-task-list', () => {
|
||||
expect((boxes[2] as HTMLInputElement).checked).toBe(true);
|
||||
expect((boxes[3] as HTMLInputElement).checked).toBe(false);
|
||||
});
|
||||
|
||||
it('oeffnet Links in einem neuen Tab, Sprungmarken im selben Fenster', () => {
|
||||
const SRC =
|
||||
'[Doku](https://example.com/doku) und https://example.org\n\n[oben](#oben)';
|
||||
|
||||
const { container } = render(
|
||||
<MDEditor.Markdown
|
||||
source={SRC}
|
||||
rehypePlugins={[[rehypeSanitize]]}
|
||||
components={{ a: NoteLink }}
|
||||
/>,
|
||||
);
|
||||
|
||||
const doku = container.querySelector('a[href="https://example.com/doku"]');
|
||||
const auto = container.querySelector('a[href="https://example.org"]');
|
||||
const hash = container.querySelector('a[href="#oben"]');
|
||||
|
||||
for (const link of [doku, auto]) {
|
||||
expect(link?.getAttribute('target')).toBe('_blank');
|
||||
expect(link?.getAttribute('rel')).toBe('noopener noreferrer');
|
||||
expect(link?.hasAttribute('node')).toBe(false);
|
||||
}
|
||||
expect(hash).not.toBeNull();
|
||||
expect(hash?.hasAttribute('target')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -115,3 +115,31 @@ export function NoteCheckbox({
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ersetzt das von react-markdown gerenderte `<a>`: Links aus Notizen oeffnen
|
||||
* immer in einem neuen Tab, damit das Dashboard nicht verlassen wird.
|
||||
* Sprungmarken innerhalb der Notiz (`#...`) bleiben im selben Fenster.
|
||||
* `node` wird wie bei NoteCheckbox nicht ins DOM durchgereicht. In der
|
||||
* Desktop-App faengt das Opener-Link-Skript `target="_blank"` ab und
|
||||
* oeffnet den Link im System-Browser.
|
||||
*/
|
||||
export function NoteLink({
|
||||
node: _node,
|
||||
href,
|
||||
children,
|
||||
...rest
|
||||
}: React.AnchorHTMLAttributes<HTMLAnchorElement> & { node?: unknown }) {
|
||||
if (href?.startsWith('#')) {
|
||||
return (
|
||||
<a href={href} {...rest}>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<a {...rest} href={href} target="_blank" rel="noopener noreferrer">
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import MDEditor, { commands } from '@uiw/react-md-editor';
|
||||
import rehypeSanitize from 'rehype-sanitize';
|
||||
import type { WidgetProps } from '../widget-registry';
|
||||
import { updateWidgetConfig } from '@/lib/dashboard-api';
|
||||
import { NoteCheckbox, toggleTaskLine } from './note-task-list';
|
||||
import { NoteCheckbox, NoteLink, toggleTaskLine } from './note-task-list';
|
||||
|
||||
const DEBOUNCE_MS = 1500;
|
||||
|
||||
@@ -156,6 +156,7 @@ export function NoteWidget({ instanceId, config }: WidgetProps) {
|
||||
onToggle={(element) => handleCheckboxToggleRef.current(element)}
|
||||
/>
|
||||
),
|
||||
a: NoteLink,
|
||||
},
|
||||
}),
|
||||
[],
|
||||
|
||||
Reference in New Issue
Block a user