Skip to content

[Feature]: Add Data Table component #146

Description

@goodbounties-nanoclaw-agent

Feature summary

Add a DataTable component to packages/ui for exact value display with typed columns,
formatting, and sorting — the complement to the visual charts, providing precise value
lookup. Pure Tamagui layout (no SVG). Fourth and final of the remaining analytics chart
components completing GoodWidget's analytics visualization layer (Scorecard already
shipped in PR #142).

Problem or opportunity

Charts are fast at showing patterns but bad at exact lookups ("what was Celo's claim count
on Tuesday?"). GoodWidget has no reusable, themeable table primitive for multi-attribute
comparisons (address + volume + tx count + date) or sorted leaderboards — each widget
currently improvises its own layout for this.

Proposed solution

Follow Scorecard.tsx's patterns (createComponent, useTheme, golden-ratio spacing,
formatMetricValue). Aesthetic reference: the StatCell pattern / stat grid in
ai-credits-widget's CreditsManagementCard.tsx.

Props / API surface (MVP)

Prop Type Required Default Description
data Array<Record<string, unknown>> Yes - Row data
columns Array<ColumnDef> Yes - Column configuration
title string No undefined Table heading
striped boolean No true Alternating row backgrounds
compact boolean No false Reduced row padding
stickyHeader boolean No true Fixed header on scroll
maxHeight number No undefined Max height before scroll
defaultSort { key, direction } No undefined Initial sort
onSort (key, direction) => void No undefined Sort callback
emptyMessage string No "No data" Empty state text
onRowPress (row, index) => void No undefined Row tap callback
variant 'bare' | 'card' No 'bare' Visual variant
testID string No undefined Testing identifier
accessibilityLabel string No auto-generated Screen reader description

ColumnDef

Field Type Required Default Description
key string Yes - Data field accessor
label string Yes - Header text
type 'text' | 'number' | 'date' | 'currency' No 'text' Formatting hint
align 'left' | 'center' | 'right' No 'center' Cell alignment
width number | string No 'auto' Column width
minWidth number No 60 Minimum width
formatter (value, row) => string No type-based Custom formatting
sortable boolean No false Enable column sorting
truncate boolean No true Truncate with ellipsis

Behavioral rules

  1. Default column alignment is 'center'; explicit align overrides.
  2. Default formatters by type — text: String(value) (truncate if needed); number: formatMetricValue (integers show no decimal); date: displayed as-is (consumer pre-formats); currency: formatMetricValue with consumer-provided prefix.
  3. Sorting: client-side when sortable=true; tapping a header cycles ascending → descending → clear, with a unicode arrow indicator; defaultSort applies on first render.
  4. Striped rows: even rows $backgroundHover, odd transparent; header fontWeight: 700.
  5. Sticky header stays fixed on vertical scroll.
  6. Horizontal overflow enables a horizontal ScrollView when columns exceed container width.
  7. Empty state: header row + single merged cell with emptyMessage.
  8. Null/undefined cell values render as "--".
  9. Rows are tappable (with press feedback) when onRowPress is provided.
  10. Compact mode reduces padding and font size.

Shared constraints (apply to all 4 remaining chart components — see Pie/Donut issue #143 for full text): Tamagui + useTheme() for layout/color (no react-native-svg needed here — table is pure layout), createComponent(), Scorecard spacing/type-scale constants, no animation, no new dependencies.

Acceptance criteria

  • Renders header + data rows matching column definitions
  • Default center alignment on all columns
  • formatMetricValue applied to number/currency columns
  • Integers show no decimal (312, not 312.0)
  • Striped rows alternate backgrounds
  • Sticky header visible during scroll
  • Sort toggles asc/desc/none on header tap with arrow indicator
  • Empty state: header + emptyMessage
  • Null cells show "--"
  • Horizontal scroll activates when needed
  • Compact mode reduces padding/font
  • Truncation: ellipsis on overflow
  • 150-row stress test: renders, scrolls smoothly, sort works
  • variant="card" works
  • testID + data-testid present
  • onRowPress fires correctly

Additional context

DO NOT:

  • Use SVG — pure Tamagui layout (YStack, XStack, Text, ScrollView)
  • Add FlatList/VirtualizedList
  • Use Icon.tsx for sort arrows — use unicode characters
  • Add pagination
  • Modify Card.ts, Text.ts, Icon.tsx, theme.ts, presets.ts, config.ts, or governance-widget

Scope boundary — only create/modify:

  1. packages/ui/src/components/DataTable.tsx
  2. packages/ui/src/index.ts (add export under // Analytics)
  3. examples/storybook/src/stories/design-system/DataTable.stories.tsx
  4. tests/design-system/smoke.spec.ts (add cases)

Mock data: multi-column top-wallets table, compact metrics summary, empty, single
column, and a 150-row stress test with maxHeight/scroll — full snippets in the spec
(link below).

References:

Create the plan

Based on the above description create an execution plan in a new sub-issue and preserve the original issue content unchanged.
Do not execute the plan until given instructions to do so.

Sub-issue requirements:

  • Title format: [DRAFT][PLAN] <what issue is being planned>
  • Type: Task
  • Description must start with: <sub-issue title>
  • Link the sub-issue back to this parent issue.

Instructions when creating the plan:

  • Map relevant files that are too be used as reference from all repos mentioned
  • import existing @GoodDollar packages
  • Map new components that should be created. Assess when a new component should be created in the new savings widget package or made part of the reusable packages/ui

Plan specification required sections:

  • Required states, flows, and behaviors
  • Execution plan
  • acceptance criteria
  • human-reviewer checklist

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Prepare AI Task

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions