Skip to content

Consolidate #108, #107, #106, #105, #104 - #109

Merged
matej21 merged 13 commits into
mainfrom
consolidate/pr-108-107-106-105-104
Sep 10, 2026
Merged

Consolidate #108, #107, #106, #105, #104#109
matej21 merged 13 commits into
mainfrom
consolidate/pr-108-107-106-105-104

Conversation

@matej21

@matej21 matej21 commented Sep 10, 2026

Copy link
Copy Markdown
Member

Combines five open PRs into one branch. Each PR's own commits are cherry-picked with -x, in the order listed.

Conflicts

None. All twelve commits applied cleanly; packages/bindx-form/src/index.ts is the only file two PRs touch, and both edits are additive exports that merged automatically.

Deviation from the originals

One extra commit, fix(bindx-form): drop the Json handler's blur re-report, resolves a semantic conflict between #107 and #108 that git could not see — the two PRs touch different files.

#107 wrote createJsonHandler.onBlur against the blur behaviour on main, where useFormInputValidationHandler cleared every error on the field. To survive that wipe, the handler re-reported its parse error on each blur. #108 narrows the blur to clear only its own html5-validity error, so the parse error now survives by itself — and the re-report appends a second copy of it. One more copy on every blur, unbounded until the next keystroke clears non-sticky errors.

The commit removes the re-report and keeps the rest of onBlur (the formatOnBlur reflow, still skipped while the input is broken).

Neither PR's tests caught this: the rendered error list does not re-render on the blur, so the duplicates were invisible to a DOM assertion. Measured on the consolidated branch before the fix — 1 error after the change, then 2, 3, 4 after successive blurs; on #107 alone it stays at 1. The added regression test reads the field errors from the store for that reason, and fails with 3 against the unfixed handler.

Verification

🤖 Generated with Claude Code

https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R

matej21 and others added 13 commits September 10, 2026 11:38
Blurring an input clears all errors on the field, so a server validation
error and a client error raised elsewhere both vanish while the value is
still invalid.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
(cherry picked from commit 27e48ac)
The handler tags the HTML5 validation message it adds with a code and clears
exactly that error, instead of resetting the whole field. Field-error clearing
now takes a FieldErrorFilter (source and/or code) in place of the source-only
argument the store already accepted but nothing passed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
(cherry picked from commit 2408c98)
'Json' was already a declared ColumnType with no handler, so a json column
fell back to the string handler: the formatter rendered [object Object] and
the parser wrote a raw string into a jsonb column.

createJsonHandler() parses the input into a JSON value, keeps the user's raw
text while it still matches the current value, keeps the previous value when
the text does not parse, and reports the failure through the new ctx.setError,
which FormInput turns into a client error on the field. That surfaces inline
and blocks persist until the text parses again.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
(cherry picked from commit 24f33c7)
Entities with c.jsonColumn() fields had no first-class form component, so
every consumer rebuilt the JSON round-trip, the seed sync and the inline
parse-error UX by hand.

JsonField mirrors TextareaField and drives the Json handler, with emptyValue,
an optional validate for shape checks, and formatOnBlur.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
(cherry picked from commit cdf99a2)
Uploader and MultiUploader hard-coded every UploaderEvents handler to a
noop, so there was no supported way to intervene between "files dropped"
and "the uploader writes into the target entity". Both now accept the
event callbacks as optional props and compose them with the internal
accept-type resolution, disconnect and extractor fill.

Uploader also gains prepareTarget: it runs once per batch, before the
target is disconnected or filled, and the target it returns is what the
upload lands on. This unblocks copy-on-write flows where a shared,
already-persisted row must be forked first instead of mutated in place.

Fixes #63

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
(cherry picked from commit 0ed377b)
…idation

prepareTarget ran before the accept check, so dropping an unacceptable
file into an uploader forked the target and then rejected every file:
the relation ended up pointing at a fresh empty entity instead of the
shared one it had. Losing a reference by dropping the wrong file is a
worse outcome than the redundant work it was meant to avoid.

It now runs after the prepare/filter phase, receives the accepted files,
and is skipped when none passed. A throw still reports the batch through
onError and abandons it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
(cherry picked from commit 60442a3)
DataViewExportTrigger built its unpaged query from filtering.resolvedWhere
only, so a grid's static `filter` prop never reached the export and the file
carried rows outside the grid's visible scope. On HasManyDataGrid it went
further: a root list query over the whole target entity table, with no parent
constraint at all.

The context now carries `fetchAllData`, and each view implements it as the
query it already loads with, minus paging — DataGrid and SelectDataView as
their list query with the combined filter, HasManyDataGrid as the same GET on
the parent record. Exposing a filter instead would leave the next view free to
forget to combine it; owning the query shape cannot be forgotten.

Fixes #67

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
(cherry picked from commit 36a8f87)
The reporter's repro covers the static filter on a root DataGrid. This adds the
has-many half: the export must read the relation through the parent record, so
it can never reach another parent's rows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
(cherry picked from commit 7d1eb6e)
DataGridToolbarUI hard-coded <DataGridAutoExport />, and the context feeding
its mobile filter chips was private, so swapping the export meant rebuilding
the toolbar and losing the mobile filters toggle. Adds an `exportControl` slot
(null hides it) and exports DataGridShowFiltersContext.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
(cherry picked from commit 6d77c89)
…ocus

A permanent dashed underline in every relation and enum cell reads as
"link" or "has a tooltip", while it actually means "click for filter
actions" — permanent noise for a power-user feature.

The decoration stays underline-based, so the wrapped-text fix from
2bbd12f is preserved; only its color is now transparent at rest.
The label becomes focusable and the tooltip opens on focus-within, so
the filter actions are reachable without a mouse.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
(cherry picked from commit bad1f2e)
The header icon used to appear only once a filter was set, so nothing
told an operator which columns can be filtered. It now always renders:
muted when idle, blue when a filter is active.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
(cherry picked from commit d8f49c5)
Consolidation fix for the interaction between #107 and #108.

#107 built createJsonHandler against a blur that wiped every error on the
field, so its onBlur re-reported the parse error to survive that wipe. #108
narrowed the blur to clear only its own html5-validity error, so the parse
error now survives on its own and the re-report adds a second copy of it —
one more on every blur, unbounded until the next keystroke clears non-sticky
errors.

Neither PR's tests caught it: the rendered error list does not re-render on
the blur, so the duplicates were only visible to a fresh read of the field.
The added test reads the field errors from the store for that reason.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Euwkf2wtutqvtE4YRutU5R
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.

2 participants