feat(timeline): gate the EventPill edit pencil behind canWrite
Thread a gate-closed canWrite prop through TimelineView -> YearBand -> EventPill and the undated bucket so a Reader never sees a dead-end edit link. canEdit now also requires canWrite; the default false keeps an un-threaded caller closed. The real boundary stays the #781 route guard plus the backend permission -- this only hides the link. Refs #842 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -51,8 +51,9 @@ describe('EventPill', () => {
|
||||
expect(srOnly?.textContent).toBe('Geburt');
|
||||
});
|
||||
|
||||
it('shows an edit affordance for a curated PERSONAL event with an eventId (REQ-008)', () => {
|
||||
it('shows an edit affordance for a curated PERSONAL event when canWrite is true (REQ-005)', () => {
|
||||
render(EventPill, {
|
||||
canWrite: true,
|
||||
entry: makeEntry({
|
||||
kind: 'EVENT',
|
||||
derived: false,
|
||||
@@ -66,11 +67,45 @@ describe('EventPill', () => {
|
||||
});
|
||||
const edit = document.querySelector('[data-testid="event-edit"]') as HTMLAnchorElement | null;
|
||||
expect(edit).not.toBeNull();
|
||||
expect(edit?.getAttribute('href')).toContain(EVENT_ID);
|
||||
expect(edit?.getAttribute('href')).toBe(`/zeitstrahl/events/${EVENT_ID}/edit`);
|
||||
});
|
||||
|
||||
it('shows no edit affordance when eventId is null (REQ-008)', () => {
|
||||
it('renders no edit affordance for a curated PERSONAL event when canWrite is false (REQ-007)', () => {
|
||||
render(EventPill, {
|
||||
canWrite: false,
|
||||
entry: makeEntry({
|
||||
kind: 'EVENT',
|
||||
derived: false,
|
||||
type: 'PERSONAL',
|
||||
eventId: EVENT_ID,
|
||||
title: 'Auswanderung',
|
||||
senderName: '',
|
||||
receiverName: '',
|
||||
documentId: undefined
|
||||
})
|
||||
});
|
||||
expect(document.querySelector('[data-testid="event-edit"]')).toBeNull();
|
||||
});
|
||||
|
||||
it('renders no edit affordance when the canWrite prop is omitted (gate-closed default) (REQ-007)', () => {
|
||||
render(EventPill, {
|
||||
entry: makeEntry({
|
||||
kind: 'EVENT',
|
||||
derived: false,
|
||||
type: 'PERSONAL',
|
||||
eventId: EVENT_ID,
|
||||
title: 'Auswanderung',
|
||||
senderName: '',
|
||||
receiverName: '',
|
||||
documentId: undefined
|
||||
})
|
||||
});
|
||||
expect(document.querySelector('[data-testid="event-edit"]')).toBeNull();
|
||||
});
|
||||
|
||||
it('shows no edit affordance when eventId is null even with canWrite (REQ-008)', () => {
|
||||
render(EventPill, {
|
||||
canWrite: true,
|
||||
entry: makeEntry({
|
||||
kind: 'EVENT',
|
||||
derived: false,
|
||||
@@ -85,8 +120,8 @@ describe('EventPill', () => {
|
||||
expect(document.querySelector('[data-testid="event-edit"]')).toBeNull();
|
||||
});
|
||||
|
||||
it('shows no edit affordance for a derived event (REQ-008)', () => {
|
||||
render(EventPill, { entry: derived('MARRIAGE', 'Heirat') });
|
||||
it('shows no edit affordance for a derived event even with canWrite (REQ-008)', () => {
|
||||
render(EventPill, { canWrite: true, entry: derived('MARRIAGE', 'Heirat') });
|
||||
expect(document.querySelector('[data-testid="event-edit"]')).toBeNull();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user