Use Columns for Row Behavior

A column should describe what a record means in this table: the label a person sees and the action available for that record.

Start With the Result

Consider a package-management table. A disabled package needs an explanation and may still offer View details while Publish is unavailable.

Package stateStatus cellActions cell
ReadyReady to publishPublish is available
DisabledDisabled with its reasonView details is shown instead of Publish

Define the Row Behavior

The following column definitions use the same package data for the status and actions cells. onPublish and onViewDetails come from the owning workflow.

import type {ColumnDef} from "@qualcomm-ui/core/table"

interface Package {
  name: string
  status: "Ready" | "Disabled"
  statusHelpText?: string
}

export function createPackageColumns(
  onPublish: (pkg: Package) => void,
  onViewDetails: (pkg: Package) => void,
): ColumnDef<Package>[] {
  return [
    {
      accessorKey: "name",
      header: "Package",
    },
    {
      accessorKey: "status",
      header: "Status",
      cell: ({row}) => {
        const pkg = row.original
        const label = pkg.status === "Ready" ? "Ready to publish" : "Disabled"

        return (
          <span>
            {label}
            {pkg.statusHelpText ? `: ${pkg.statusHelpText}` : null}
          </span>
        )
      },
    },
    {
      id: "actions",
      header: "Actions",
      cell: ({row}) => {
        const pkg = row.original

        if (pkg.status === "Disabled") {
          return (
            <button onClick={() => onViewDetails(pkg)} type="button">
              View details
            </button>
          )
        }

        return (
          <button onClick={() => onPublish(pkg)} type="button">
            Publish
          </button>
        )
      },
    },
  ]
}

The status column turns a stored value into the label and help text needed by this table. The actions column reads the package status and chooses the matching action and callback.

This is business logic in columns: rules that determine a row's visible state and available interaction.

Consume the Column Factory

Pass the table screen's two action handlers to createPackageColumns, then pass its result directly to useReactTable.

import {useMemo} from "react"

import {getCoreRowModel} from "@qualcomm-ui/core/table"
import {useReactTable} from "@qualcomm-ui/react/table"

function usePackagesTable(
  packages: Package[],
  onPublish: (pkg: Package) => void,
  onViewDetails: (pkg: Package) => void,
) {
  const columns = useMemo(
    () => createPackageColumns(onPublish, onViewDetails),
    [onPublish, onViewDetails],
  )

  return useReactTable({
    columns,
    data: packages,
    getCoreRowModel: getCoreRowModel(),
  })
}

The component that calls usePackagesTable renders the returned table with the usual table markup. When a person chooses an action, the cell calls the matching handler with the exact Package supplied by the screen.

Keep the Workflow Behind the Callback

The callback's business logic does not need to live in the column. onPublish can open a confirmation dialog, start a request, report an error, and refresh the table data. onViewDetails can navigate to the package page.

The column needs only the rule and the typed callback.

Last updated on by Ryan Bower