Skip to content

DataGridIsDefinedFilterControls writes nullCondition but createIsDefinedFilterHandler reads defined — clicks never activate the filter #36

@jindrak02

Description

@jindrak02

Summary

DataGridIsDefinedFilterControls and createIsDefinedFilterHandler are wired together by DataGridIsDefinedColumn, but they read and write different fields on the filter artifact — the controls write nullCondition (via DataViewNullFilterTriggeruseDataViewNullFilter), while the handler reads defined. Clicking the ✓/✗ buttons in the popover therefore never activates the handler and the filter has no effect on the data.

Environment

Reproduction

import { createIsDefinedFilterHandler } from '@contember/bindx'

const handler = createIsDefinedFilterHandler('email')

// What `DataGridIsDefinedFilterControls`'s ✓ button actually writes onto the
// artifact (via `useDataViewNullFilter` action="toggleExclude"):
const afterExcludeClick = { ...handler.defaultArtifact(), nullCondition: false }

handler.isActive(afterExcludeClick) // → false (expected: true)
handler.toWhere(afterExcludeClick)  // → undefined (expected: { email: { isNull: false } })

The same mismatch occurs end-to-end inside a grid:

<DataGrid entity={User}>
  {it => (
    <>
      <DataGridIsDefinedColumn field={it.tenantPersonId} header="Has identity" />
    </>
  )}
</DataGrid>

Clicking ✓ or ✗ inside the column header popover updates the artifact's nullCondition field but leaves defined: null. useFilteringState.hasActiveFilters stays false, resolvedWhere stays undefined, and the table doesn't filter.

Expected behavior

Either the controls or the handler should change to agree on the field. Every other filter type in filterHandlers.test.ts uses nullCondition as the canonical "include nullable bucket" toggle — text, number, date, enum, relation all support { ..., nullCondition: boolean } artifacts (see existing text filter > with null condition test).

createIsDefinedFilterHandler is the outlier with its { defined: boolean | null } shape.

Actual behavior

Default artifact: { defined: null }isActive returns false, toWhere returns undefined.

After the ✓ button click (useDataViewNullFilter toggleExclude branch in filterHooks.ts):

setFilter(it => ({ ...it, nullCondition: it?.nullCondition === false ? undefined : false }))

Artifact becomes { defined: null, nullCondition: false }. Handler's isActive and toWhere both look at artifact.defined only (see filterHandlers.ts) — they ignore nullCondition entirely. isActive returns false, toWhere returns undefined, and the where clause for the GraphQL query has no contribution from this filter.

Suspected root cause

Two changes were probably made on different days without cross-checking:

  1. IsDefinedFilterArtifact was introduced as { defined: boolean | null } — see packages/bindx/src/dataview/filterArtifacts.ts.
  2. DataGridIsDefinedFilterControls was built on top of DataViewNullFilterTrigger, which had already standardized on nullCondition.

Neither side was wrong on its own. The integration just wasn't covered by a test — DataGridIsDefinedColumn has no usage in the repo, so the broken combination went unnoticed.

Suggested fix

Pick one of the two conventions and align both sides. The least-disruptive option is to make createIsDefinedFilterHandler read nullCondition, since every other filter handler already supports that field and DataViewNullFilterTrigger is generic infrastructure used by many components:

// packages/bindx/src/dataview/filterHandlers.ts
export interface IsDefinedFilterArtifact {
  readonly nullCondition?: boolean
}

export function createIsDefinedFilterHandler(fieldPath: string): FilterHandler<IsDefinedFilterArtifact> {
  return {
    defaultArtifact: () => ({}),
    isActive: artifact => artifact.nullCondition !== undefined,
    toWhere: artifact => artifact.nullCondition === undefined
      ? undefined
      : buildNestedWhere(fieldPath, { isNull: artifact.nullCondition }),
  }
}

Semantics map: nullCondition: false ≡ "exclude nulls" = is-defined; nullCondition: true ≡ "include nulls only" = not-defined; undefined ≡ off. That matches the existing ✓/✗ button copy in DataGridIsDefinedFilterControls.

If keeping defined is preferred, the controls / useDataViewNullFilter need a parallel toggleDefined action and the IsDefined column needs its own dedicated trigger.

Either fix should come with a DataGridIsDefinedColumn integration test under tests/react/dataview/ that asserts the click actually flips hasActiveFilters.

Workaround shipped downstream

We applied a temporary workaround in our project, marked
TODO [BindX] (<this-issue-url>): <description>. The workaround is a tiny two-button filter UI in our admin (IsDefinedFilterButtons) that writes { defined: boolean | null } directly via the column's setArtifact, bypassing DataGridIsDefinedFilterControls/DataViewNullFilterTrigger. We will remove this once the controls and handler agree.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions