Show a parent's name once per calendar week row, not once per band
Ad-hoc days are banded per leave type and adjacent wishes are separate bands, so a type change inside one parent's row repeated "Name · Type". Only the parent's leftmost band in the row now carries the name; the band aria-label still includes it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1155,6 +1155,95 @@ describe('CalendarWeek', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('band names: once per parent per week row, not once per segment', () => {
|
||||
it('shows the name once when ad-hoc days for one parent split into a vacation run then a sjukpenning run', () => {
|
||||
// Mon/Tue vacation and Wed-Fri sjukpenning are two separate ad-hoc
|
||||
// bands (bandKeyFor keys on parent+dayType), not one band with a
|
||||
// mid-period type change — each has its own single-run label. The
|
||||
// parent's name belongs only on the leftmost (vacation) one.
|
||||
const plannedDays = [
|
||||
pd({ id: '1', date: '2026-01-05', parentPersonalDataUuid: 'parent-1', dayType: 'vacation' }),
|
||||
pd({ id: '2', date: '2026-01-06', parentPersonalDataUuid: 'parent-1', dayType: 'vacation' }),
|
||||
pd({ id: '3', date: '2026-01-07', parentPersonalDataUuid: 'parent-1', dayType: 'sjukpenning' }),
|
||||
pd({ id: '4', date: '2026-01-08', parentPersonalDataUuid: 'parent-1', dayType: 'sjukpenning' }),
|
||||
pd({ id: '5', date: '2026-01-09', parentPersonalDataUuid: 'parent-1', dayType: 'sjukpenning' }),
|
||||
]
|
||||
const { container } = render(<CalendarWeek {...defaultProps} plannedDays={plannedDays} parentName="Test Parent" />)
|
||||
expect(screen.getAllByTestId(/^calendar-band-/)).toHaveLength(2)
|
||||
const labels = Array.from(container.querySelectorAll('.calendar-band__label'))
|
||||
expect(labels).toHaveLength(2)
|
||||
expect(labels[0]?.textContent).toContain('Test Parent')
|
||||
expect(labels[0]?.textContent).toContain('Semester')
|
||||
expect(labels[1]?.textContent).not.toContain('Test Parent')
|
||||
expect(labels[1]?.textContent).toContain('Sjukpenning')
|
||||
})
|
||||
|
||||
it('shows the name once across two adjacent wishes for the same parent', () => {
|
||||
const plannedDays = [
|
||||
pd({ id: '1', date: '2026-01-05', wishId: 'wish-1', parentPersonalDataUuid: 'parent-1' }),
|
||||
pd({ id: '2', date: '2026-01-06', wishId: 'wish-1', parentPersonalDataUuid: 'parent-1' }),
|
||||
pd({ id: '3', date: '2026-01-07', wishId: 'wish-2', parentPersonalDataUuid: 'parent-1' }),
|
||||
pd({ id: '4', date: '2026-01-08', wishId: 'wish-2', parentPersonalDataUuid: 'parent-1' }),
|
||||
]
|
||||
const { container } = render(<CalendarWeek {...defaultProps} plannedDays={plannedDays} parentName="Test Parent" />)
|
||||
expect(screen.getAllByTestId(/^calendar-band-/)).toHaveLength(2)
|
||||
const labels = Array.from(container.querySelectorAll('.calendar-band__label'))
|
||||
expect(labels).toHaveLength(2)
|
||||
expect(labels[0]?.textContent).toContain('Test Parent')
|
||||
expect(labels[1]?.textContent).not.toContain('Test Parent')
|
||||
})
|
||||
|
||||
it('shows each parent\'s name once, independently, when both parents have a mid-week type change', () => {
|
||||
const plannedDays = [
|
||||
pd({ id: '1', date: '2026-01-05', parentPersonalDataUuid: 'parent-1', dayType: 'vacation' }),
|
||||
pd({ id: '2', date: '2026-01-06', parentPersonalDataUuid: 'parent-1', dayType: 'sjukpenning' }),
|
||||
pd({ id: '3', date: '2026-01-07', parentPersonalDataUuid: 'other-parent', dayType: 'vacation' }),
|
||||
pd({ id: '4', date: '2026-01-08', parentPersonalDataUuid: 'other-parent', dayType: 'sjukpenning' }),
|
||||
]
|
||||
const { container } = render(<CalendarWeek {...defaultProps} plannedDays={plannedDays} parentName="Test Parent" />)
|
||||
const labels = Array.from(container.querySelectorAll('.calendar-band__label'))
|
||||
expect(labels).toHaveLength(4)
|
||||
expect(labels[0]?.textContent).toContain('Test Parent')
|
||||
expect(labels[1]?.textContent).not.toContain('Test Parent')
|
||||
expect(labels[1]?.textContent).not.toContain('Partner')
|
||||
expect(labels[2]?.textContent).toContain('Partner')
|
||||
expect(labels[3]?.textContent).not.toContain('Partner')
|
||||
expect(labels[3]?.textContent).not.toContain('Test Parent')
|
||||
})
|
||||
|
||||
it('still shows the name on a band continuing into the next week row (each row recomputes its own leftmost segment)', () => {
|
||||
const plannedDays = [
|
||||
pd({ id: '1', date: '2026-01-10', wishId: 'wish-1', parentPersonalDataUuid: 'parent-1' }),
|
||||
pd({ id: '2', date: '2026-01-13', wishId: 'wish-1', parentPersonalDataUuid: 'parent-1' }),
|
||||
]
|
||||
const calendarDaysByDate = new Map<string, CalendarDayEntry>([
|
||||
['2026-01-11', calDay({ date: '2026-01-11', dayType: 'weekend' })],
|
||||
['2026-01-12', calDay({ date: '2026-01-12', dayType: 'weekend' })],
|
||||
])
|
||||
const { container } = render(
|
||||
<>
|
||||
<CalendarWeek
|
||||
{...defaultProps}
|
||||
plannedDays={plannedDays}
|
||||
calendarDaysByDate={calendarDaysByDate}
|
||||
parentName="Test Parent"
|
||||
/>
|
||||
<CalendarWeek
|
||||
{...defaultProps}
|
||||
weekDates={['2026-01-12', '2026-01-13', '2026-01-14', '2026-01-15', '2026-01-16', '2026-01-17', '2026-01-18']}
|
||||
plannedDays={plannedDays}
|
||||
calendarDaysByDate={calendarDaysByDate}
|
||||
parentName="Test Parent"
|
||||
/>
|
||||
</>,
|
||||
)
|
||||
const labels = Array.from(container.querySelectorAll('.calendar-band__label'))
|
||||
expect(labels).toHaveLength(2)
|
||||
expect(labels[0]?.textContent).toContain('Test Parent')
|
||||
expect(labels[1]?.textContent).toContain('Test Parent')
|
||||
})
|
||||
})
|
||||
|
||||
describe('per-day hover/focus card', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
|
||||
@@ -452,6 +452,34 @@ export function CalendarWeek({
|
||||
[weekDates, plannedDays, calendarDaysByDate],
|
||||
)
|
||||
|
||||
// A parent's leave in one week row can be split across several band
|
||||
// segments — ad-hoc days are keyed by parent+dayType (`bandKeyFor`), so a
|
||||
// Mon-Tue vacation stretch and a Wed-Fri sjukpenning stretch for the same
|
||||
// parent are two segments, not one — and separate wishes are always
|
||||
// separate segments regardless of adjacency. Showing the parent name on
|
||||
// every segment's first run would repeat "Name · Type" once per segment;
|
||||
// the spec is ONE name per parent per week row, on that parent's
|
||||
// leftmost segment (lowest `startCol`, tie-broken by lowest `lane` for
|
||||
// determinism when two segments happen to start in the same column).
|
||||
// Every other segment/run for that parent in this row shows the type
|
||||
// only. This is computed per week (each row calls `computeBandSegments`
|
||||
// independently), so a band continuing into the next week row still gets
|
||||
// its own leftmost-segment name there.
|
||||
const nameSegmentIdByParent = useMemo(() => {
|
||||
const bestByParent = new Map<string, BandSegment>()
|
||||
for (const seg of bandSegments) {
|
||||
const current = bestByParent.get(seg.parentUuid)
|
||||
if (!current || seg.startCol < current.startCol || (seg.startCol === current.startCol && seg.lane < current.lane)) {
|
||||
bestByParent.set(seg.parentUuid, seg)
|
||||
}
|
||||
}
|
||||
const map = new Map<string, string>()
|
||||
for (const [parentUuid, seg] of bestByParent) {
|
||||
map.set(parentUuid, seg.id)
|
||||
}
|
||||
return map
|
||||
}, [bandSegments])
|
||||
|
||||
// Bands overlay the day-cell row itself (grid-row: 1, position: absolute)
|
||||
// instead of occupying separate rows below it — see the comment on
|
||||
// `.calendar-week` in calendar.css. `barHeight`/`laneGap`/`bandBottomInset`
|
||||
@@ -679,6 +707,7 @@ export function CalendarWeek({
|
||||
const runDubbeldag = segment.dayInfos
|
||||
.slice(run.startIdx, run.endIdx + 1)
|
||||
.some((d) => !d.isGap && d.isDubbeldag)
|
||||
const showName = runIdx === 0 && nameSegmentIdByParent.get(segment.parentUuid) === segment.id
|
||||
return (
|
||||
<div
|
||||
key={`label-${run.startIdx}`}
|
||||
@@ -693,7 +722,7 @@ export function CalendarWeek({
|
||||
leaveType={runTypeLabel}
|
||||
color={getColorForType(runDayType)}
|
||||
isDubbeldag={runDubbeldag}
|
||||
parentLabel={runIdx === 0 ? label : undefined}
|
||||
parentLabel={showName ? label : undefined}
|
||||
paintBackground={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user