Skip to content

Extract a shared SegmentedToggle primitive for ViewModeToggle/DensityToggle/SettingsPopover #3426

Description

@JSONbored

Context

apps/ui/src/components/metagraphed/view-mode-toggle.tsx (ViewModeToggle, lines 16-60) and apps/ui/src/components/metagraphed/density-toggle.tsx (DensityToggle, lines 11-56) are two independently hand-rolled role="tablist"/role="tab" segmented switches with byte-for-byte identical wrapper classes ("inline-flex items-center rounded-md border border-border bg-card p-0.5") and identical button classes ("inline-flex items-center gap-1.5 rounded px-2 py-1 text-[11px] font-medium transition-colors min-h-8" with the same active/inactive branch). apps/ui/src/components/metagraphed/settings-popover.tsx has its own local SegmentBtn/SegmentedRow helpers (lines 118-154) implementing the same visual segmented-control look but via aria-pressed toggle buttons rather than role="tablist"/aria-selected tabs — three call sites, no shared base, and two subtly different ARIA contracts for what should be one pattern.

Requirement

Extract a single shared SegmentedToggle primitive (generic over an options array + active value + onChange) that captures the common wrapper/button markup and styling, and have ViewModeToggle and DensityToggle render through it while preserving their existing public props and role="tablist"/role="tab"/aria-selected semantics. SettingsPopover's internal SegmentBtn/SegmentedRow should also switch to the same primitive if it can do so without changing its aria-pressed toggle semantics; if the ARIA shapes can't be reconciled without an API compromise, wire the primitive through ViewModeToggle/DensityToggle only and leave SettingsPopover as a documented follow-up rather than forcing a mismatched semantic onto it.

Deliverable

  • Add apps/ui/src/components/ui/segmented-toggle.tsx (new file, alongside the existing shared-primitive convention in that directory, e.g. popover.tsx, accordion.tsx) exporting a SegmentedToggle component that takes an options list ({ value, label, Icon? }[]), the active value, an onChange, aria-label, and className, and renders the shared wrapper + per-option button markup with role="tablist"/role="tab"/aria-selected (matching the current ViewModeToggle/DensityToggle markup exactly, byte-for-byte, so no visual/behavioral diff).
  • Edit apps/ui/src/components/metagraphed/view-mode-toggle.tsx: replace the inline <div role="tablist">...</div> block (current lines 29-58) with a call into SegmentedToggle, keeping ViewModeToggle's existing exported signature (value, onChange, options?, className) and the ViewMode type untouched so none of its four call sites (apps/ui/src/routes/subnets.index.tsx, apps/ui/src/routes/surfaces.tsx, apps/ui/src/routes/providers.index.tsx, apps/ui/src/routes/endpoints.tsx) need to change.
  • Edit apps/ui/src/components/metagraphed/density-toggle.tsx: replace the inline <div role="tablist">...</div> block (current lines 25-54) with the same SegmentedToggle call, keeping DensityToggle's existing exported signature (value, onChange, className) so its one call site (apps/ui/src/routes/subnets.index.tsx) is unaffected.
  • If reconciled per the Requirement: edit apps/ui/src/components/metagraphed/settings-popover.tsx to route its SegmentBtn/SegmentedRow (lines 118-154) through SegmentedToggle too, or delete those two local helpers if fully subsumed. If not reconciled, leave settings-popover.tsx untouched and say so explicitly in the PR description.
  • "Done" = one new primitive file, ViewModeToggle and DensityToggle both delegating their markup to it, zero changes to any of their four call sites' JSX/props, and no visual regression (identical rendered DOM/classes for the tablist pattern).

Acceptance criteria

  • apps/ui/src/components/ui/segmented-toggle.tsx is added and exports a reusable SegmentedToggle component
  • apps/ui/src/components/metagraphed/view-mode-toggle.tsx renders through SegmentedToggle instead of its own inline role="tablist" markup, with its exported props/type unchanged
  • apps/ui/src/components/metagraphed/density-toggle.tsx renders through SegmentedToggle instead of its own inline role="tablist" markup, with its exported props/type unchanged
  • None of the four existing call sites (apps/ui/src/routes/subnets.index.tsx, apps/ui/src/routes/surfaces.tsx, apps/ui/src/routes/providers.index.tsx, apps/ui/src/routes/endpoints.tsx) have their ViewModeToggle/DensityToggle usage changed
  • The diff either updates apps/ui/src/components/metagraphed/settings-popover.tsx to use the new primitive too, or the PR description states explicitly why SettingsPopover was left as-is (ARIA-shape mismatch)
  • role="tablist" / role="tab" / aria-selected and the active/inactive Tailwind classes are byte-identical to current behavior (before/after screenshots of the affected toggles show no visual diff)

Non-goals

Size

Size: S — keep this PR small (aim for ≤10 files / ≤1000 LOC).

Part of #2542.

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix or unsolicited PR — scores a 0.05x multiplier.

    Projects

    Status
    In progress

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions