Skip to content

feat: SW-2547 Export Progress and Snippet from the barrel, fix Progress value forwarding - #198

Open
owilliams-tetrascience wants to merge 6 commits into
mainfrom
SW-2547-export-progress-snippet
Open

feat: SW-2547 Export Progress and Snippet from the barrel, fix Progress value forwarding#198
owilliams-tetrascience wants to merge 6 commits into
mainfrom
SW-2547-export-progress-snippet

Conversation

@owilliams-tetrascience

@owilliams-tetrascience owilliams-tetrascience commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Progress and Snippet are fully built and story-tested in this kit, but neither
was listed in src/index.ts. Consumers importing them got undefined — which
blocked a conformance meter (Progress) and an output-path copy button
(Snippet). This adds both to the barrel.

// Before: undefined at runtime
import { Progress, Snippet } from "@tetrascience-npm/tetrascience-react-ui";

While writing the Progress story I found a real accessibility defect in the
component and fixed it — details in the aside below.

Closes SW-2547 (epic SW-2301,
React UI Kit v1.1.0).

The exports mechanism — correcting the ticket

The ticket states "the exports map has no subpaths". That isn't accurate as
written: package.json's exports already has ./ui/*, ./composed/*,
./charts/*, ./ai/*, ./utils/* and ./lib/* wildcards from SW-2007.

The real mechanism is that scripts/build/component-entries.ts derives
per-component build entries by parsing the export * from "@/..." lines in
src/index.ts. A component absent from that barrel gets no entry generated,
so the wildcard has nothing to resolve to. Net effect matches the ticket
(unreachable for consumers), but the fix is the barrel line — no package.json
change was needed.

Both components are single files in ui/, that category's dominant file shape, so
the wildcard resolves types as well as runtime. Verified against a real build
rather than assumed, since a shape mismatch fails silently for types while runtime
imports keep working:

Resolution target progress snippet
dist/ui/<name>.js (import)
dist/ui/<name>.cjs (require)
dist/components/ui/<name>.d.ts (types)

Snippet was also miscategorised

Its story was titled "AI Elements/Snippet" while the component lives in
src/components/ui/. It's a read-only input-group field with a copy button —
nothing AI-specific; the title was inherited from upstream ai-elements, and
src/components/ai/ in this kit is otherwise entirely chat/LLM primitives.

Retitled to Components/Data Display/Snippet. The component stays in ui/
no file move, so no subpath churn and no break for anyone already deep-importing
./ui/snippet. Existing Zephyr testCaseId values are untouched.


🐛 Aside: the Progress defect (found while writing its story)

src/components/ui/progress.tsx destructured value out of its props but never
forwarded it to ProgressPrimitive.Root — it was only used to compute the
indicator's inline translateX:

function Progress({ className, value, ...props }) {
  return (
    <ProgressPrimitive.Root data-slot="progress" {...props}>   {/* no value! */}
      <ProgressPrimitive.Indicator
        style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
      />
    </ProgressPrimitive.Root>
  );
}

Radix therefore never received a value. Every bar rendered
data-state="indeterminate" with no aria-valuenow, regardless of what the
consumer passed.

Before After
Visual bar position Correct Correct (unchanged)
aria-valuenow absent "60"
data-state at value={60} indeterminate loading
data-state at value={100} indeterminate complete

The bar looked right because the style transform drives it independently of
Radix state — which is exactly why this went unnoticed. But screen readers, and
any consumer branching on data-state, saw a permanently indeterminate bar. This
matters directly for the conformance meter this ticket unblocks: it would have
been visually correct but unreadable to assistive tech.

Fix is one line (value={value}). It is load-bearing, not speculative — deleting
it fails 4 of the 7 new play tests with aria-valuenow === null.

Consumer requirement worth flagging: role="progressbar" must have an
accessible name and the component cannot invent one, so every call site needs its
own aria-label (or aria-labelledby). Storybook's axe check
(a11y: { test: "error" }) fails a story that omits it. Now documented in
DESIGN.md §4.


Notes for the reviewer

Two deliberate, opposite calls on the SW-2305 / #197 taxonomy. That PR
(#197) is still open, so there was nothing to rebase onto:

Snippet was missing from the DESIGN.md inventory table too, so it's added there
as well.

Split into two commits so the fix is revertable independently of the exports:
fix: for the Progress value forwarding, feat: for the barrel additions.

Type of Change

  • Feature (new functionality) — Progress / Snippet exports
  • Bug fix — Progress never forwarded value to Radix
  • Refactor
  • Documentation — DESIGN.md inventory rows + a11y requirement
  • Chore (build, CI, dependencies)
  • Breaking change

Checklist

  • yarn lint passes
  • yarn build passes
  • yarn test:all passes — 171 files, 1616 tests
  • Storybook stories added/updated — 7 new Progress stories; Snippet retitled
  • Code coverage remains the same or increased — progress.tsx at 100%

Testing

yarn lint, yarn typecheck, yarn build and yarn test:all all clean locally
(171 files / 1616 tests). Coverage on the one changed source file,
progress.tsx: 100% statements / branches / functions / lines.

The 7 new Progress stories (Default, Empty, Complete, Indeterminate, WithLabel,
Sizes, SemanticColors) have play tests asserting aria-valuenow and data-state.

Zephyr test cases:

  • Snippet — existing, unchanged: SW-T4731, SW-T4732, SW-T4733, SW-T4734
  • Progressnew, IDs not yet generated. Per repo convention testCaseId is
    left absent; needs the zephyr_sync label applied to this PR to generate the 7
    IDs and commit them back to the branch.

Verification

  • Deploys to preview environment for manual verification
  • All CI/E2E checks pass

Screenshots

Not applicable — the Progress fix changes only the ARIA/data-state attributes
Radix emits. There is no visual change: the bar's rendered position was already
correct via the inline style transform, which is what masked the bug.

🤖 Generated with Claude Code

Progress destructured `value` out of its props but never passed it to
ProgressPrimitive.Root — it was only used to compute the indicator's
inline translateX. Radix therefore never received a value, so every bar
rendered with data-state="indeterminate" and no aria-valuenow no matter
what the consumer passed.

The bar still *looked* right because the style transform drives it
independently of Radix state, which is why this went unnoticed. Screen
readers and any consumer reading data-state saw an indeterminate bar.

Adds a 7-story Storybook file whose play tests assert aria-valuenow and
data-state; reverting the one-line fix fails 4 of the 7.

Note for consumers: role="progressbar" requires an accessible name and
the component cannot invent one, so each story passes its own
aria-label. Documented in DESIGN.md in the follow-up commit.
Both components are fully built and story-tested in this kit but were
absent from src/index.ts, so `import { Progress } from
"@tetrascience-npm/tetrascience-react-ui"` resolved to undefined. This
blocked a consumer's conformance meter (Progress) and output-path copy
button (Snippet).

The barrel line is the whole fix. scripts/build/component-entries.ts
derives the per-component subpath entries by parsing the
`export * from "@/..."` lines in src/index.ts, so a component missing
from the barrel gets no build entry and the existing `./ui/*` exports
wildcard has nothing to resolve to. Verified post-build that all six
resolution targets now exist for each component:
dist/ui/<name>.{js,cjs} and dist/components/ui/<name>.d.ts. Both are
single files in ui/, that category's dominant shape, so no package.json
change was needed.

Also retitles the Snippet story from "AI Elements/Snippet" to
"Components/Data Display/Snippet". The component lives in
components/ui/ and is a read-only input-group field with a copy button —
nothing AI-specific; the misfiled title was inherited from upstream
ai-elements. Existing zephyr testCaseId values are untouched.

DESIGN.md: adds the Progress and Snippet inventory rows, and documents
that Progress requires a consumer-supplied aria-label because
role="progressbar" must be named and the component cannot name itself.
Copilot AI lite review requested due to automatic review settings September 1, 2026 14:40
@vercel

vercel Bot commented Sep 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
ts-lib-ui-kit-storybook Ready Ready Preview Sep 10, 2026 1:39am UTC

Request Review

@unblocked unblocked Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No issues found

About Unblocked

Unblocked has been set up to automatically review your team's pull requests to identify genuine bugs and issues.

📖 Documentation — Learn more in our docs.

💬 Ask questions — Mention @unblocked to request a review or summary, or ask follow-up questions.

👍 Give feedback — React to comments with 👍 or 👎 to help us improve.

⚙️ Customize — Adjust settings in your preferences.

@owilliams-tetrascience owilliams-tetrascience changed the title SW-2547 Export Progress and Snippet from the barrel, fix Progress value forwarding feat: SW-2547 Export Progress and Snippet from the barrel, fix Progress value forwarding Sep 1, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR makes Progress and Snippet available to consumers via the library barrel export (src/index.ts) and fixes Progress’s accessibility/state wiring by forwarding the value prop to the Radix root. It also adds/updates Storybook coverage and documentation to reflect the components’ intended taxonomy and accessibility requirements.

Changes:

  • Export Progress and Snippet from src/index.ts so consumers can import them from the package root.
  • Fix Progress to forward value to ProgressPrimitive.Root (restoring correct aria-valuenow / data-state behavior).
  • Add Progress stories with play assertions, retitle Snippet story, and document Progress’s accessible-name requirement in DESIGN.md.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/index.ts Adds barrel exports for Progress and Snippet so they’re available to consumers.
src/components/ui/progress.tsx Forwards value to the Radix progress root to fix ARIA/state behavior.
src/components/ui/progress.stories.tsx Adds Storybook coverage + play tests for determinate/indeterminate behavior and labeling.
src/components/ui/snippet.stories.tsx Retitles story to the Components/Data Display taxonomy.
DESIGN.md Adds Progress/Snippet to the inventory and documents the accessible-name requirement for Progress.
Suppressed comments (1)

src/components/ui/progress.stories.tsx:134

  • Avoid hard-coded Tailwind palette colors in stories; use semantic tokens (e.g. bg-warning) so the example remains consistent with the design system and dark mode.
        className="bg-orange-500/20 [&_[data-slot=progress-indicator]]:bg-orange-500"

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/components/ui/progress.stories.tsx Outdated
The story hard-coded bg-green-500 / bg-orange-500 instead of the kit's
semantic tokens. Those Tailwind palette colors are not redefined under
.dark, so the story rendered the same washed-out greens/oranges in dark
mode, while --positive / --warning / --destructive are defined for both
themes with documented AA contrast ratios.

Switches to bg-positive / bg-warning, matching the existing in-repo
precedent for exactly this pattern in src/components/ai/context.tsx,
which styles a Progress indicator via the same
[&_[data-slot=progress-indicator]] selector. The destructive row already
used the token.

Raised by Copilot on the PR.
Resolves a DESIGN.md §3 inventory conflict. SW-2526 (Text) and SW-2540
(Tree) both landed on main and appended rows to the Data Display block in
the same position as this branch's Snippet row. Kept both sides — the two
changes are independent and purely additive — ordering Snippet before Text
after Kbd.

src/index.ts auto-merged; verified progress and snippet are still in their
alphabetical positions and that the two pre-existing out-of-alphabetical
trailing entries (code-editor, tetrascience-icon) are unchanged from main
rather than introduced here.
SW-2305 (#197) landed and rewrote the whole DESIGN.md §3 inventory table
to the 7-category taxonomy, conflicting with this branch's added rows —
the conflict this branch anticipated.

Resolved by taking main's rewritten table wholesale and re-adding only the
Progress row, now under "Feedback & Status" (alphabetically between Banner
and Skeleton) rather than the old "Feedback". The Snippet row was not
re-added: #197's rewrite already includes one, so keeping main's version
avoids a duplicate.

No story retitling was needed. This branch deliberately authored both
stories in #197's target taxonomy ahead of it landing, and both now match
exactly — DESIGN.md's new "Storybook title" guidance even names
`Components/Data Display/Snippet` as the correct title for Snippet, which
is what this branch had already changed it to.

Verified after merge: lint, typecheck, test:all (180 files / 1738 tests)
all clean; the §4 accessibility note added by this branch survived intact;
and all six per-component subpath resolution targets still build.
@github-actions

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🟢 Lines 96.94% (🎯 83%)
🟰 ±0%
24687 / 25466
🟢 Statements 96.94% (🎯 83%)
🟰 ±0%
24687 / 25466
🟢 Functions 94.61% (🎯 74%)
🟰 ±0%
1177 / 1244
🟢 Branches 90.69% (🎯 81%)
🟰 ±0%
4961 / 5470
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/components/ui/progress.tsx 100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
100%
🟰 ±0%
Generated in workflow #1210 for commit 05527d8 by the Vitest Coverage Report Action

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants