Skip to content

library: enforce 21-object-per-shelf cap in the UI#150

Merged
MaryWylde merged 2 commits into
devfrom
feat/library-animation
Jun 17, 2026
Merged

library: enforce 21-object-per-shelf cap in the UI#150
MaryWylde merged 2 commits into
devfrom
feat/library-animation

Conversation

@MaryWylde

Copy link
Copy Markdown
Contributor

Surface the backend's per-shelf object limit: catch the 400 on object create, edit (shelf change), and move, mapping the "shelf cannot have more than 21 objects" message to friendly copy. Pre-disable the Add control and show a counter once a shelf is full, with the backend remaining the source of truth.

Bundles related library refinements: CharCount atom, AudioCard, ReorderGrid, AddShelfModal, and addObjectSchema field limits.

Surface the backend's per-shelf object limit: catch the 400 on object
create, edit (shelf change), and move, mapping the "shelf cannot have
more than 21 objects" message to friendly copy. Pre-disable the Add
control and show a counter once a shelf is full, with the backend
remaining the source of truth.

Bundles related library refinements: CharCount atom, AudioCard,
ReorderGrid, AddShelfModal, and addObjectSchema field limits.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@MaryWylde

Copy link
Copy Markdown
Contributor Author

@claude , review this PR.

@claude

claude Bot commented Jun 17, 2026

Copy link
Copy Markdown

Claude finished @MaryWylde's task in 3m 10s —— View job


PR Review: library — enforce 21-object-per-shelf cap in the UI

Tasks

  • Read trigger comment and classify request
  • Diff changed files vs origin/dev
  • Read key source files
  • Write review findings
  • Post final comment

The approach is sound — isShelfFullError + SHELF_FULL_MESSAGE as shared singletons, a regex-based backend message matcher, and belt-and-suspenders client pre-disable + server-side error surfacing. A few things worth addressing before merge.


Issues

SHELF_NAME_MAX_LENGTH duplicated

Shelf.tsx:134 and AddShelfModal.tsx:18 both declare:

const SHELF_NAME_MAX_LENGTH = 100;

This PR added it to AddShelfModal.tsx (CharCount work) but left it as a second local copy. If the backend changes this limit, both have to be updated in sync — the exact problem this PR solves for MAX_OBJECTS_PER_SHELF. Move SHELF_NAME_MAX_LENGTH to common.ts to keep it consistent.

Fix this →


Empty-shelf "Add" button not guarded

Shelf.tsx:529 (the + button shown when objects.length === 0):

<Button
  onClick={openAdd}      // ← no disabled prop
  type={ButtonType.Secondary}
  Icon={<PlusIcon />}
  ariaLabel={`Add ${typeLabel}`}
/>

Today this is safe — atObjectLimit = objects.length >= 21, so it can never be true when the empty state is showing. But the header "Add" button explicitly receives disabled={atObjectLimit}, and the asymmetry is a consistency gap. At minimum add disabled={atObjectLimit} to mirror the header button. Fix this →


CharCount.module.scss — redundant text-align: left

CharCount.module.scss:7: text-align: left is the default for block elements and can be removed. Fix this →


Minor observations (not blocking)

  • openAdd double-gate (Shelf.tsx:252): the early-return guard is redundant since a disabled button won't fire onClick. Safe to keep, but could be cleaned up.
  • CharCount named exportexport function CharCount + export * from './CharCount' deviates from the index.ts convention (import X from './X'; export default X;). The existing Text atom does the same thing, so this is consistent with the de-facto practice for atoms that export multiple symbols. Worth normalising in a follow-up but not a blocker here.
  • OBJECT_FIELD_LIMITS in src/utils/ (legacy location) — pre-existing, not introduced by this PR.

Positives

  • isShelfFullError regex (/shelf cannot have more than \d+ objects/i) is deliberately forward-compatible with backend cap changes — good defensive pattern.
  • SHELF_FULL_MESSAGE uses the template literal over MAX_OBJECTS_PER_SHELF, so the string and the constant stay in sync automatically.
  • All three mutation paths (create, edit with shelf change, move-to) are handled — no gap where a 400 would silently swallow.
  • OBJECT_FIELD_LIMITS exported alongside the zod schema means the CharCount indicator max can never drift from what validation actually enforces.
  • aria-hidden="true" on CharCount is correct — the counter is supplementary; validation errors already convey the limit breach to screen readers.
  • SVG </symbol> fix is a good catch.

Verdict: Two small fixes before merge — the SHELF_NAME_MAX_LENGTH duplication and the empty-shelf button guard. Everything else is a nitpick or follow-up.

Dedupe SHELF_NAME_MAX_LENGTH into common.ts (was a second local copy in
Shelf and AddShelfModal), guard the empty-shelf Add button with the same
atObjectLimit check as the header button, and drop a redundant
text-align: left from CharCount.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@MaryWylde MaryWylde merged commit 4c43891 into dev Jun 17, 2026
2 checks passed
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