Summary
DataGridHasOneColumn offers the include/exclude filter actions on a cell whose nullable has-one relation is empty. The actions filter on the relation's disconnected placeholder id (__placeholder_…), so „Exclude" sends { not: { <field>: { id: { eq: "__placeholder_…" } } } }. A Contember engine rejects that value as an invalid id, and the grid fails to load.
Environment
Reproduction
// Article.author is hasOne('Author', { nullable: true }); one row has author: null
<DataGrid entity={schema.Article}>
{it => (
<>
<DataGridHasOneColumn field={it.author} header="Author">
{author => author.name.value ?? '—'}
</DataGridHasOneColumn>
<DataGridAutoTable />
</>
)}
</DataGrid>
The test asserts that the connected cell has a [data-bindx-tooltip] trigger and the empty cell has none. The second assertion fails.
Expected behavior
A cell with no related entity renders its content without the filter affordance. This is what the if (!id) return content branch in renderCellWrapper intends. A filter value must be an id the server knows.
Actual behavior
The empty cell („—") is wrapped in DataGridHasOneTooltip with id="__placeholder_<uuid>". Hovering it shows „Filter" / „Exclude". Clicking „Exclude" stores notId: ["__placeholder_<uuid>"]. The has-one filter handler turns it into { not: { author: { id: { eq: "__placeholder_<uuid>" } } } }, which the engine rejects (not a UUID). „Filter" produces the same invalid eq condition.
Suspected root cause
packages/bindx-ui/src/datagrid/columns/has-one-column.tsx:108-110:
renderCellWrapper: ({ content, item, fieldName, filterName, fieldRef }: RelationCellWrapperContext) => {
const id = getRelatedAccessor(item, fieldName)?.id ?? null
if (!id) return content
For a disconnected has-one, getRelatedAccessor returns the relation handle, and HasOneHandle.id (packages/bindx/src/handles/HasOneHandle.ts:292-299) returns the placeholder id, which is truthy. So the guard never triggers.
has-many-column.tsx has the same code, but a has-many handle exposes no id, so that wrapper returns content for every cell and this bug does not affect it.
Suggested fix
Guard on a persisted id. Select.tsx and select-field.tsx already check isPlaceholderId for the same reason:
const id = getRelatedAccessor(item, fieldName)?.id
if (!id || !isPersistedId(id)) return content
isPersistedId also excludes a temporary id, which the server cannot filter on either. The empty value stays filterable through the column's „N/A" (null) filter.
Workaround shipped downstream
None. The reporting project waits for a release with the fix; a PR with the fix follows.
Summary
DataGridHasOneColumnoffers the include/exclude filter actions on a cell whose nullable has-one relation is empty. The actions filter on the relation's disconnected placeholder id (__placeholder_…), so „Exclude" sends{ not: { <field>: { id: { eq: "__placeholder_…" } } } }. A Contember engine rejects that value as an invalid id, and the grid fails to load.Environment
@contember/bindx@0.1.52/@contember/bindx-ui@0.1.52(version installed in the reporting project); the code is unchanged in 0.1.53contember/bindx@mainas of7ae3d15tests/react/dataview/hasOneColumnEmptyCellFilter.test.tsxbug/has-one-column-empty-cell-filterReproduction
The test asserts that the connected cell has a
[data-bindx-tooltip]trigger and the empty cell has none. The second assertion fails.Expected behavior
A cell with no related entity renders its content without the filter affordance. This is what the
if (!id) return contentbranch inrenderCellWrapperintends. A filter value must be an id the server knows.Actual behavior
The empty cell („—") is wrapped in
DataGridHasOneTooltipwithid="__placeholder_<uuid>". Hovering it shows „Filter" / „Exclude". Clicking „Exclude" storesnotId: ["__placeholder_<uuid>"]. The has-one filter handler turns it into{ not: { author: { id: { eq: "__placeholder_<uuid>" } } } }, which the engine rejects (not a UUID). „Filter" produces the same invalideqcondition.Suspected root cause
packages/bindx-ui/src/datagrid/columns/has-one-column.tsx:108-110:For a disconnected has-one,
getRelatedAccessorreturns the relation handle, andHasOneHandle.id(packages/bindx/src/handles/HasOneHandle.ts:292-299) returns the placeholder id, which is truthy. So the guard never triggers.has-many-column.tsxhas the same code, but a has-many handle exposes noid, so that wrapper returnscontentfor every cell and this bug does not affect it.Suggested fix
Guard on a persisted id.
Select.tsxandselect-field.tsxalready checkisPlaceholderIdfor the same reason:isPersistedIdalso excludes a temporary id, which the server cannot filter on either. The empty value stays filterable through the column's „N/A" (null) filter.Workaround shipped downstream
None. The reporting project waits for a release with the fix; a PR with the fix follows.