feat: decouple person-mention display text from person name (#372) #373
@@ -79,17 +79,6 @@ let leftBorderClass = $derived(
|
||||
saveState === 'error' ? 'border-l-2 border-error' : active ? 'border-l-2 border-turquoise' : ''
|
||||
);
|
||||
|
||||
// Single source of truth for the editor's textarea — stored on attach so
|
||||
// we can read selection bounds for quote selection without re-querying the DOM.
|
||||
let textareaEl: HTMLTextAreaElement | null = null;
|
||||
|
||||
function captureTextarea(node: HTMLTextAreaElement) {
|
||||
textareaEl = node;
|
||||
return () => {
|
||||
textareaEl = null;
|
||||
};
|
||||
}
|
||||
|
||||
function emitChange() {
|
||||
onTextChange(localText, localMentions);
|
||||
}
|
||||
@@ -101,17 +90,6 @@ async function handleDelete() {
|
||||
});
|
||||
if (confirmed) onDeleteClick();
|
||||
}
|
||||
|
||||
function handleTextareaMouseUp() {
|
||||
if (!textareaEl) return;
|
||||
const start = textareaEl.selectionStart;
|
||||
const end = textareaEl.selectionEnd;
|
||||
if (start !== end) {
|
||||
selectedQuote = localText.substring(start, end);
|
||||
} else {
|
||||
selectedQuote = null;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -176,24 +154,22 @@ function handleTextareaMouseUp() {
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Textarea (now powered by PersonMentionEditor for @-mention typeahead) -->
|
||||
<div onmouseup={handleTextareaMouseUp} role="presentation">
|
||||
<PersonMentionEditor
|
||||
bind:value={() => localText,
|
||||
(v) => {
|
||||
localText = v;
|
||||
emitChange();
|
||||
}}
|
||||
bind:mentionedPersons={() => localMentions,
|
||||
(next) => {
|
||||
localMentions = next;
|
||||
emitChange();
|
||||
}}
|
||||
placeholder={m.transcription_block_placeholder()}
|
||||
onfocus={onFocus}
|
||||
captureTextarea={captureTextarea}
|
||||
/>
|
||||
</div>
|
||||
<!-- Editor powered by PersonMentionEditor (Tiptap) for @-mention typeahead -->
|
||||
<PersonMentionEditor
|
||||
bind:value={() => localText,
|
||||
(v) => {
|
||||
localText = v;
|
||||
emitChange();
|
||||
}}
|
||||
bind:mentionedPersons={() => localMentions,
|
||||
(next) => {
|
||||
localMentions = next;
|
||||
emitChange();
|
||||
}}
|
||||
placeholder={m.transcription_block_placeholder()}
|
||||
onfocus={onFocus}
|
||||
onSelectionChange={(text) => (selectedQuote = text)}
|
||||
/>
|
||||
|
||||
{#if selectedQuote}
|
||||
<p class="mt-1 text-xs text-ink-3">{m.transcription_block_quote_hint()}</p>
|
||||
|
||||
@@ -41,8 +41,7 @@ describe('TranscriptionBlock — rendering', () => {
|
||||
|
||||
it('renders text in textarea', async () => {
|
||||
renderBlock();
|
||||
const textarea = page.getByRole('textbox');
|
||||
await expect.element(textarea).toHaveValue('Liebe Mutter,');
|
||||
await expect.element(page.getByText('Liebe Mutter,')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders optional label when provided', async () => {
|
||||
@@ -226,14 +225,18 @@ describe('TranscriptionBlock — delete confirmation', () => {
|
||||
// ─── Quote selection ─────────────────────────────────────────────────────────
|
||||
|
||||
describe('TranscriptionBlock — quote selection', () => {
|
||||
it('shows quote hint after text is selected in textarea', async () => {
|
||||
it('shows quote hint after text is selected in the editor', async () => {
|
||||
renderBlock({ text: 'Breslau, den 12. August' });
|
||||
await page.getByRole('textbox').click();
|
||||
// Select text and fire mouseup via native DOM — locator.selectText/dispatchEvent not available
|
||||
const el = document.querySelector('textarea') as HTMLTextAreaElement;
|
||||
el.focus();
|
||||
el.setSelectionRange(0, el.value.length);
|
||||
el.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
|
||||
// Select all text in the contenteditable via the native Selection API.
|
||||
// Tiptap fires selectionUpdate which the block forwards as onSelectionChange.
|
||||
const editorEl = document.querySelector('[role="textbox"]') as HTMLElement;
|
||||
const range = document.createRange();
|
||||
range.selectNodeContents(editorEl);
|
||||
const selection = window.getSelection()!;
|
||||
selection.removeAllRanges();
|
||||
selection.addRange(range);
|
||||
editorEl.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
await expect.element(page.getByText(/Zitat/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user