Skip to content

Add a typed meta slot to column leaves and forward it from built-in columns #126

Description

@matej21

Summary

Add a typed extension slot to column leaves. ColumnLeafProps declares no place for per-column data that the code consuming useDataViewContext().columns needs, so an extra prop on a custom leaf reaches columns only through the undocumented ...leaf spread in resolveColumnNames, and the built-in column factories drop it.

Environment

Reproduction

A downstream spreadsheet export needs each computed column to say how its cell is written as text. The column type owns that knowledge, so it has to travel on the leaf:

declare module '@contember/bindx-dataview' {
	interface ColumnLeafProps {
		// Not part of the bindx contract: survives only because resolveColumnNames spreads `...leaf`.
		readonly exportCell?: ((row: ExportRow) => string | null) | null
	}
}

const leafProps: ColumnLeafProps = {
	name: 'composed',
	fieldName: null,
	fieldRef: null,
	sortingField: null,
	filterName: null,
	filterHandler: undefined,
	isTextSearchable: false,
	header: 'Composed',
	renderCell: accessor => <ComposedCell accessor={accessor} />,
	exportCell: row => composedText(row),
}
return <ColumnLeaf {...leafProps} />

// consumer
const { columns } = useDataViewContext()
columns.map(column => column.exportCell)

The failing test expresses the contract we would like instead, a meta slot typed by an interface the consumer augments:

declare module '@contember/bindx-dataview' {
	interface ColumnLeafMeta {
		readonly exportHeader?: string
	}
}

<ColumnLeaf {...leafProps} meta={{ exportHeader }} />
<DataGridTextColumn field={it.title} header="Title" meta={{ exportHeader: 'Title (export)' }} />

const { columns } = useDataViewContext()
columns.map(it => it.meta?.exportHeader)

Expected behavior

  • ColumnLeafProps has a documented slot for consumer data, typed by an augmentable interface, so no consumer has to augment ColumnLeafProps itself.
  • The slot reaches DataViewColumn through resolveColumnNames and the runtime-bound columns built in useDataGridSetup.
  • Every built-in column component (createColumn, createRelationColumn, the enum columns) accepts the slot and forwards it to its leaf.

Actual behavior

  • bun run typecheck rejects the test:

    tests/react/dataview/columnLeafMeta.test.tsx(54,5): error TS2353: Object literal may only specify known properties, and 'meta' does not exist in type 'ColumnLeafProps'.
    tests/react/dataview/columnLeafMeta.test.tsx(63,79): error TS2339: Property 'meta' does not exist on type 'DataViewColumn'.
    tests/react/dataview/columnLeafMeta.test.tsx(100,59): error TS2322: Type '{ field: FieldRef<string> & FieldAccessor<string>; header: string; meta: { exportHeader: string; }; }' is not assignable to type 'IntrinsicAttributes & ColumnComponentProps<string> & object'.
      Property 'meta' does not exist on type 'IntrinsicAttributes & ColumnComponentProps<string> & object'.
    
  • At runtime the custom leaf's extra prop does reach columns (the first test passes), but only as a side effect of the spread. The built-in column drops it (the second test fails):

    Expected: "title:Title (export)"
    Received: "title:-"
    

Suspected root cause

  • packages/bindx-dataview/src/columnLeaf.ts: ColumnLeafProps is a closed interface with no consumer slot. resolveColumnNames passes unknown props through only because it builds each column with { ...leaf, name }.
  • packages/bindx-dataview/src/createColumn.ts (createColumnStaticRender) and packages/bindx-dataview/src/createRelationColumn.tsx build leafProps field by field from the component props, so a prop they do not list never reaches the leaf.

Suggested fix

  • In columnLeaf.ts, export interface ColumnLeafMeta {} and add readonly meta?: ColumnLeafMeta to ColumnLeafProps (and so to DataViewColumn), documented as the place for consumer data.
  • Add meta?: ColumnLeafMeta to ColumnComponentProps and forward props['meta'] in createColumnStaticRender, in createRelationColumn and in the enum columns in columns.tsx.

A documented guarantee that resolveColumnNames and useDataGridSetup keep unknown leaf props would also work, but it leaves the consumer augmenting a core interface and does not cover the built-in columns.

Workaround shipped downstream

We applied a temporary workaround in our project, marked
TODO [BindX] (<this-issue-url>): <description>. The workaround augments ColumnLeafProps with an exportCell property and relies on resolveColumnNames spreading ...leaf; only our own column components set it. We will remove it once this issue is resolved.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions