Skip to content

fix: 1.1.1 — a calendar with zero resources no longer crashes or renders blank - #1

Open
blade47 wants to merge 1 commit into
masterfrom
fix/zero-resource-calendar
Open

fix: 1.1.1 — a calendar with zero resources no longer crashes or renders blank#1
blade47 wants to merge 1 commit into
masterfrom
fix/zero-resource-calendar

Conversation

@blade47

@blade47 blade47 commented Aug 7, 2026

Copy link
Copy Markdown
Owner

What

Mounting the scheduler with an empty resources array and non-default resourceFields breaks two views. Found downstream in production: the day view reloaded the browser and the week view rendered an empty grid.

Three defects that only combine into a failure under those conditions:

1. Day's placeholder ignored resourceFields

Day.tsx fabricates a placeholder so the view still renders with no resources, but keyed it literally:

const defaultResource = [{ id: 'default', text: 'Default' }];

Every reader of a resource resolves its fields through resourceFields. A consumer that remaps them — { idField: 'resourceid', textField: 'name' } — gets a placeholder that no reader can see. Now built from resourceFields.

2. ResourceHeader dereferenced the text field unguarded

text: resource[resourceFields.textField],   // undefined for the placeholder above
...
{text.charAt(0).toUpperCase()}              // TypeError

So the invisible placeholder became a crash. This is the serious one: a render-phase throw is not contained by the library. React unmounts the tree and the consumer's error boundary handles it — which is how one malformed resource became a whole-page failure downstream (their boundary calls window.location.reload() in production, hence "clicking Day refreshes the page").

Coalesced to ''. The declared text: string was always a claim about consumer-supplied data that the type cannot enforce.

3. tabMode was true even when no tabs were rendered

Week short-circuits WithResources when resources.length === 0, so no tab card exists — but Scheduler still passed tabMode={resourceViewMode === 'tabs'}. The bounded+tabMode rule in css.ts targets & > div:first-of-type believing it to be that card:

'& > div:first-of-type': { overflow: 'visible', flex: '0 0 auto', height: 'auto', minHeight: '100%' },

With no tabs that selector hits WeekTable's sticky header grid and stretches it to minHeight: 100%, so the hour rows are rendered but pushed a full viewport below the fold — a calendar that looks empty until you scroll. Now requires resources.length > 0, letting an empty calendar fall through to the correct resourceCount <= 1 stacking branch.

Testing

  • New ResourceHeader.test.tsx covers the resilient half — verified it fails without fix 2 and passes with it. A second case pins that a present text field still renders its name and avatar initial, so the null-coalescing can't silently swallow real data.
  • Full suite: 30/30. tsc --noEmit clean. eslint and prettier --check clean on all touched files.

Fixes 1 and 3 are behavioural in a zero-resource layout, which no existing test covered; a rendering test for Day/Week under bounded height would need a taller harness than this repo currently has, so I've left that out rather than bolt on a fragile one.

Note

The Scheduler.tsx diff also carries a one-line prettier normalization that was already sitting uncommitted in the working tree — it falls inside the hunk being touched, and the file is prettier-clean with it included.

🤖 Generated with Claude Code

…ers blank

Three defects that only surface together, when a consumer mounts the scheduler with
an empty resources array and non-default resourceFields. Downstream this took a
production page down: the day view reloaded the browser and the week view rendered an
empty grid.

1. Day's zero-resource placeholder was keyed by literal {id, text} while every reader
   of a resource resolves fields through resourceFields. A consumer that remaps them
   (e.g. {idField:'resourceid', textField:'name'}) got a placeholder no reader could
   see. Now built from resourceFields.

2. ResourceHeader dereferenced resource[textField] unguarded via .charAt(0), so the
   invisible placeholder became a TypeError. A render-phase throw is not contained by
   the library — React unmounts and the CONSUMER's error boundary takes over, which is
   how one malformed resource became a whole-page failure. Coalesced to ''; the
   declared `text: string` was a claim about consumer data this type cannot enforce.

3. tabMode was true whenever resourceViewMode was 'tabs', even with no resources — but
   Week short-circuits WithResources when resources are empty, so the tab card never
   exists. The bounded+tabMode rule targets `& > div:first-of-type` believing it to be
   that card, and instead stretched the sticky header grid to minHeight 100%, pushing
   the hour rows a full viewport below the fold. Now requires resources to exist, so an
   empty calendar falls through to the correct resourceCount<=1 stacking branch.

Patch, not minor: no API surface changes, and all three are corrections to behaviour
in a state that previously threw or mis-rendered.

New ResourceHeader tests cover the resilient half; verified they fail without fix 2.
Full suite 30/30, tsc and eslint clean, `npm run build` (which prepublishOnly runs)
succeeds and all three fixes verified present in dist/index.js.

Note: the Scheduler.tsx diff also carries a one-line prettier normalization that was
already uncommitted in the working tree — it was in the hunk being touched, and the
file is prettier-clean with it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@blade47
blade47 force-pushed the fix/zero-resource-calendar branch from 578d00a to c01cdb9 Compare August 7, 2026 08:37
@blade47 blade47 changed the title fix: a calendar with zero resources no longer crashes or renders blank fix: 1.1.1 — a calendar with zero resources no longer crashes or renders blank Aug 7, 2026
@blade47

blade47 commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

Bumped package.json to 1.1.1 so this can be published straight from the merge.

Patch rather than minor: no API surface changes, and all three are corrections to behaviour in a state that previously threw or mis-rendered. npm currently has 1.1.0, and the version is folded into this commit rather than a separate one, matching how 1.1.0 and 1.0.10 were released here.

Verified npm run build (what prepublishOnly runs) succeeds and all three fixes are present in the built dist/index.js — including that the old hardcoded id:"default",text:"Default" placeholder is gone (0 occurrences).

Note that 1.1.0 was never tagged (latest tag is v1.0.10), so I haven't created a v1.1.1 tag — happy to add one if you'd rather restart that habit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant