Skip to content

1. Custom Columns Prop (w/ Default Renderer & Dot Path Utility) #442

Description

@MitchellShiell

Introduce a customColumns optional prop on DictionaryTableViewer and implement the MetaValueRenderer component. Both are exported from the package public API and form the complete contract that all downstream tickets implement against.

customColumns prop

The prop accepts an array of column configurations. Each entry is either a path-based config (columnHeader + metaPath) or a component-based config (columnHeader + columnComponent). The prop is entirely optional; when omitted, the component behaves identically to the current implementation.

Both modes can be mixed freely within the same array:

<DictionaryTableViewer
	customColumns={[
		{ columnHeader: 'FHIR Mapping', metaPath: 'meta.mappings.FHIR' },
		{ columnHeader: 'All Mappings', columnComponent: AllMappingsCell },
	]}
/>

MetaValueRenderer component

Accepts a single value of any type and renders it appropriately for display in a table cell:

  • String: plain text; strings that are valid URLs render as clickable hyperlinks opening in a new tab
  • Number and boolean: their string representation
  • Array: items as a comma-separated list
  • Object: key-value pairs as readable formatted text
  • Undefined or null: nothing, producing an empty cell

Long values that overflow the cell are truncated and use our already implemented show more design pattern.

Exported so developers can use it inside their own columnComponent implementations when they want standard display behaviour without writing their own type-handling logic:

const FhirMappingCell = ({ field }: { field: SchemaField }) => <MetaValueRenderer value={field.meta?.mappings?.FHIR} />;

The `MetaValueRenderer1 component will be used by default for a custom column if no custom component is specified

getByDotPath utility

Extract getByDotPath from its current private implementation inside DictionaryTableViewer.tsx into a shared, exported location. The utility resolves a dot-notation path string against a SchemaField object and returns the value at that path, or undefined if any key in the chain is absent. It must never throw.

It serves two roles: internally by the framework to resolve metaPath values for path-based columns, and as an export for developers who want safe nested property access inside their own columnComponent implementations:

// Used internally by the framework for path-based columns:
const value = getByDotPath(field, 'meta.mappings.FHIR');

// Also usable by developers in component-based columns:
const FhirMappingCell = ({ field }: { field: SchemaField }) => (
	<MetaValueRenderer value={getByDotPath(field, 'meta.mappings.FHIR')} />
);

Acceptance Criteria

  • customColumns is accepted as an optional prop on DictionaryTableViewer
  • Each entry in the array is either a path-based config (columnHeader + metaPath) or a component-based config (columnHeader + columnComponent)
  • Omitting the prop produces no change to existing behaviour
  • TypeScript rejects entries that provide both metaPath and columnComponent, or neither
  • MetaValueRenderer handles all supported value types without errors
  • URL strings render as anchor elements that open in a new tab
  • Arrays and objects produce readable output that does not cause cell overflow
  • Values exceeding the available cell width are truncated with the full content accessible on hover
  • Undefined and null values produce an empty cell with no placeholder text
  • MetaValueRenderer is exported from the package public API
  • Valid dot-paths resolve to the correct value on a SchemaField object
  • Paths that traverse missing or null intermediate keys return undefined without throwing
  • getByDotPath is exported from the package public API
  • The existing private implementation in DictionaryTableViewer.tsx is replaced with a reference to the extracted utility, with no change in behaviour

Dependencies

None

Activity

  1. MitchellShiell commented on Apr 7, 2026

    @MitchellShiell
    ContributorAuthor

    Jons Comments

    Transferring comments here (ticket would not transfer easily from admin to lectern)


    Thank you for explicitly calling out for the creation of a MetaValueRenderer component.

    I would call out the following requirement:

    • This component will be used by default for a custom column if no custom component is specified

    Also, I agree that it should be exported, but I don't understand the purpose of your example usage.


    • Each entry in the array is either a path-based config (columnHeader + metaPath) or a component-based config (columnHeader + columnComponent)
    • Omitting the prop produces no change to existing behaviour
    • TypeScript rejects entries that provide both metaPath and columnComponent, or neither

    I think there is value to creating a custom component that gets a value read from a metaPath. As an example, you could create a component that renders a set of known values using an icon+link, and then you want to reuse that component for different meta properties. With your requirements, I would need a custom component for each meta property. Instead, I recommend that we allow both custom component and the metaPath properties for a custom column.

    Each custom component should accept the following props:

    Property Type Description
    metaPath string the metaPath string property given to the custom component.
    value DictionaryMeta Type from Lectern or undefined The value at the metaPath for the field.
    field SchemaField Type from Lectern The full field definition from the schema.

    The requirement for a custom column should be that it requires either metaPath or a custom component, or both.

    • metaPath only : use MetaValueRenderer component
    • component only : use the component, provide metaPath as undefined and value as undefined, but the field value will still have the full field details
    • both : use the custom component, provide the metaPath string and the value (if available) from the metadata

    Arrays and objects produce readable output that does not cause cell overflow
    Values exceeding the available cell width are truncated with the full content accessible on hover

    This is a vague requirement. It is not clear what the max cell size will be, as row height depends on screen width and the content of the other columns, and column width is undefined at present.

    For arrays, perhaps we want to reuse the existing unordered list implementation that has the view more behaviour?

    How do you plan to handle simple objects #like:

    {
      "optionA": "value 1",
      "optionB": "value 2",
    }

    How about objects with mixed value types like:

    {
      "optionA": "value 1",
      "optionB": 987654321,
      "optionC": ["a", "b", "c"],
      "optionD": [123, 456, 789],
    }

    And lastly, how d oyou want to render deeply nested objects:

    {
      "value": "value 1",
      "withNested": {
        "value": 123,
        "mostNested": {
          "value": ["a", "b", "c"],
          "moreNested": {
             "valuest": "important data, don't forget me!"
           }
        }
      }
    }
  2. added a commit that references this issue on Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

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