fix(journey-editor): selectedPersons change calls markDirty via $effect
Skip-first-run $effect tracks selectedPersons array length; any add/remove after mount marks the editor dirty so the unsaved-warning fires on nav. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -32,6 +32,7 @@ let { geschichte, onSubmit, submitting = false }: Props = $props();
|
||||
const unsaved = createUnsavedWarning();
|
||||
|
||||
let title = $state(geschichte.title ?? '');
|
||||
|
||||
let body = $state(geschichte.body ?? '');
|
||||
let status: 'DRAFT' | 'PUBLISHED' = $state(geschichte.status ?? 'DRAFT');
|
||||
let selectedPersons: PersonOption[] = $state(
|
||||
@@ -64,6 +65,17 @@ const alreadyAddedIds = $derived(
|
||||
const canPublish = $derived(items.length > 0 && !titleEmpty);
|
||||
const showPublishedEmptyWarning = $derived(status === 'PUBLISHED' && items.length === 0);
|
||||
|
||||
// Skip the initial run so mounting with pre-existing persons doesn't mark dirty.
|
||||
let _personEffectMounted = false;
|
||||
$effect(() => {
|
||||
void selectedPersons.length;
|
||||
if (!_personEffectMounted) {
|
||||
_personEffectMounted = true;
|
||||
return;
|
||||
}
|
||||
unsaved.markDirty();
|
||||
});
|
||||
|
||||
let listEl: HTMLElement | null = $state(null);
|
||||
let editorColEl: HTMLElement | null = $state(null);
|
||||
|
||||
|
||||
@@ -678,6 +678,44 @@ describe('JourneyEditor — unsaved warning banner', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('JourneyEditor — selectedPersons marks dirty', () => {
|
||||
function getNavCallback() {
|
||||
const calls = vi.mocked(beforeNavigate).mock.calls;
|
||||
const [callback] = calls[calls.length - 1];
|
||||
return (cancel = vi.fn()) => {
|
||||
(callback as (nav: { cancel: () => void; to: { url: URL } | null }) => void)({
|
||||
cancel,
|
||||
to: { url: new URL('http://localhost/geschichten') }
|
||||
});
|
||||
return cancel;
|
||||
};
|
||||
}
|
||||
|
||||
it('removing a person chip marks the editor dirty', async () => {
|
||||
render(
|
||||
JourneyEditor,
|
||||
defaultProps({
|
||||
geschichte: makeGeschichte({
|
||||
persons: [{ id: 'p1', firstName: 'Anna', lastName: 'Schmidt' }]
|
||||
})
|
||||
})
|
||||
);
|
||||
|
||||
// Confirm navigation is NOT blocked initially (clean state)
|
||||
const triggerNav = getNavCallback();
|
||||
expect(triggerNav()).not.toHaveBeenCalled();
|
||||
|
||||
// Remove the person chip (aria-label = m.comp_multiselect_remove() = "Entfernen")
|
||||
await userEvent.click(page.getByRole('button', { name: m.comp_multiselect_remove() }));
|
||||
|
||||
// After person removal, navigation should be blocked
|
||||
await vi.waitFor(() => {
|
||||
const cancel = triggerNav();
|
||||
expect(cancel).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('JourneyEditor — person chips from GeschichteView', () => {
|
||||
it('renders person names in the sidebar chips (PersonView carries no displayName)', async () => {
|
||||
render(
|
||||
|
||||
Reference in New Issue
Block a user