Repository navigation
fix(plugin-view): an authored event-name onChange / onViewChange no longer crashes filter-ui, sort-ui and view-switcher through SchemaRenderer (objectui#10616) - #10646
Conversation
… a function, so an authored event name no longer crashes through SchemaRenderer (objectui#10616) filter-ui, sort-ui and view-switcher declare onChange / onViewChange as a window event NAME (objectui#6124). SchemaRenderer spreads node keys as React props, so the authored string also reached the same-named callback prop, and `onChange?.(next)` threw "onChange is not a function" before the event was dispatched. One shared helper now calls the prop only when it is a function and dispatches the event named on the schema; a host function is still called first. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
|
changeset-claim-re-read
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
…d, so the 10616 changeset says it passes these keys through, not every node key Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #10616
Clause-②: no
Implemented by the
os-devrun that thedomain:uiseat 2 dispatched, sessionhttps://claude.ai/code/session_014mXUNuFomfj24w7s1pZzhN. Readings were taken at BASEeca276070, theorigin/maincommit the worktree was cut from. Every gate below ran at HEAD528b56d6e, which is this branch's head when the PR opens.What changed
filter-ui,sort-uiandview-switcherdeclareonChange/onViewChangeas a window event NAME (objectui#6124).SchemaRendererpasses every non-metadata node key to the component as a React prop, and then spreads the host's own props last. With no host prop, the authored string therefore lands in the component's callback prop of the same name. Each control called that prop withonChange?.(next)before dispatching. The first interaction threwTypeError: onChange is not a function(onViewChangeon the switcher), and the window event never fired.packages/plugin-view/src/viewHandlerChannels.tsholds one helper,notifyViewHandlerChannels, and all three controls call it from theirnotifyChange. It calls the prop only whentypeofsays it is a function. Then, if the schema names an event, it dispatchesnew CustomEvent(schema.KEY, { detail }). The event name always comes fromschema, never from the prop slot. One helper means the three controls cannot drift apart.FilterUI.tsx,SortUI.tsxandViewSwitcher.tsx,notifyChangenow calls the helper with the documented detail:{ values },{ sort }or{ view }. The exported prop types, the zod mirror and the registrations do not change.SchemaRendereris not touched.SchemaRendererspreads the host's props last, and the authored name is still onschema.handlerEventNameLiveness.6124.test.tsx: only the header comment changed. It had three cross-file line addresses into lines this diff moves. They are now symbol citations, and the header adds a note that the file mocksSchemaRendererplus a pointer to the new pin. No assertion changed..changeset/10616-view-handler-event-names-through-renderer.md:'@object-ui/plugin-view': patch.Pins: through the real
SchemaRendererand registry, with no mockpackages/plugin-view/src/__tests__/handlerEventNameThroughRenderer-10616.test.tsxhas six tests, two for each control:detail, and nothing is reported. The test reads the windowerrorevent,console.errorand any thrown error, because React 19 reports a handler error rather than rethrowing it out offireEvent.SchemaRenderernext to the same string is called once with the new value, and the event still fires.Red on base, green on head. All five legs ran in one locked run at HEAD
528b56d6e. The base leg checks out the three components at BASE and keeps the pins from HEAD.Tests 14 passed (14)Tests 3 failed / 11 passed (14). Each authored-string leg fails:reportedholdsTypeError: onChange is not a functionfor filter-ui and sort-ui, andTypeError: onViewChange is not a functionfor view-switcher. The three controls and all eight objectui#6124 tests pass.FilterUI.tsxat BASE1 failed / 5 passed (6): only the filter-ui authored leg failsSortUI.tsxat BASE1 failed / 5 passed (6): only the sort-ui authored leg failsViewSwitcher.tsxat BASE1 failed / 5 passed (6): only the view-switcher authored leg failsnotifyViewHandlerChannelsoccurs 0 times and a bareonChange?.(oronViewChange?.(occurs once.EXIT INT TERMtrap. It was proven by comparing each file's hash with its HEAD blob and by an emptygit diff HEAD.The objectui#6124 pins stay. They complement the new pins rather than duplicate them:
.shapeand thedescribe()text).undefinedthere.Gates (HEAD
528b56d6e)pnpm exec vitest run packages/plugin-view/:Test Files 50 passed (50),Tests 483 passed (483).pnpm --filter @object-ui/plugin-view type-check(tsc --noEmit && tsc -p tsconfig.test.json) ran afterturbo run build --filter='@object-ui/plugin-view^...' --concurrency=2, which reportedTasks: 15 successful, 15 total. Result: exit 0.tsc -p tsconfig.test.json --listFilesOnlyconfirms that the test program contains the new pin, the objectui#6124 pin andviewHandlerChannels.ts.eslint --no-inline-config --format jsonreports 6 files, 0 errors and 11 warnings. All 11 warnings sit on lines this diff does not touch (no-explicit-any,set-state-in-effect), and lint sets no--max-warnings. Three facts support the narrowing:eslint.config.js: thefiles: ['**/*.{ts,tsx}']block covers all six files, and none is ignored.parserOptionsorprojectService, so linting is not type-aware. No rule ineslint-rules/*.jsreads the filesystem (0 hits forreadFileSync,existsSync,readdirSyncorstatSync; control: 9 of 9 rule files matchcreate).check-changeset-presence,check:new-line-citations(0 new citation(s)),check:control-bytes,check-changeset-no-major,check-changeset-overwrite,check-changeset-fixed,check-changeset-claims,check:handler-key-reads,check:unreferenced-sources,check:esm-specifiers,check:phantom-deps,check:self-import,check-vi-mock-override-shape,check-vi-mock-specifiers,check-vi-mock-inheritandcheck-test-path-roots.content/or README file.check-governed-queue-guard --teston the seven paths:NOT GOVERNED.git merge-tree --write-tree HEAD origin/mainatf99f9cde7: clean.origin/mainhas not changedpackages/plugin-viewor the objectui#6124 changeset since BASE.The pending objectui#6124 changeset
.changeset/6124-view-handler-keys-are-event-names.mdpublishes the authored form as a working example. At this change every sentence in it is true: the renderer dispatchesnew CustomEvent(schema.onViewChange, …)and its two siblings, and thesort-uiexample and its listener now work throughSchemaRenderer. The file is therefore unchanged: its md5 isccd92197c632d2b200478176ac6d1a95at BASE and at HEAD.check-changeset-claimsreports that changeset's last paragraph because it names the objectui#6124 test file, which this diff edits. That report is report-only. The edit only touches the header comment and the pin still holds both halves, so the paragraph stays true.Acceptance notes
content/docs/api/schema-reference.md,ViewSwitcherSchematable: theonViewChangerow reads "Expression or callback invoked on view change." The objectui#6124describe()text refuses exactly that reading. The row was false before this diff and is still false after it. It is outside the claimed file surface, so it is not changed here and is reported to the seat instead.onChange?: string;(filter-ui.mdx,sort-ui.mdx) andonViewChange?: string;(view-switcher.mdx) are true at this change. The plugin-view README names neither key.packages/plugin-vieworviews.zod.tshas this shape:views.zod.tscarries exactly these three event-name keys. The one otherEvent namedescribe underpackages/types/src/zodisActionSchema.tracking.eventincrud.zod.ts. It is nested undertracking, so it is never spread as a prop, and no component calls a same-named prop.Generated by Claude Code