Skip to content

docs(storybook): story the object browser — file tree, breadcrumbs, and object detail - #521

Merged
alukach merged 6 commits into
mainfrom
stories/object-browser
Aug 28, 2026
Merged

alukach merged 6 commits into
mainfrom
stories/object-browser

Conversation

@alukach

@alukach alukach commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Stories for the object browser — the file tree, the breadcrumbs above it, and the detail panel for a single object. 16 stories across three components.

The __filename fix that made any of this renderable went in separately as #524, which has merged; this PR is stories plus one Storybook config line.

DirectoryList — and it browses

Default is navigable. In the app a directory is a <Link> and the server re-fetches the listing for the new URL. Storybook has neither a server nor a real router, so clicks went nowhere and every story was one frozen level.

Default now holds the prefix in state, derives each listing from a single fixture tree, and intercepts clicks on links into this product. That is a stand-in for the routing, not the routing itself — the story says so — but it makes the thing you'd actually want to check reviewable.

Verified in a browser rather than assumed:

  • rootrecordings2019 shows the two recordings;
  • the breadcrumb walks back up through recordings to root;
  • clicking a file leaves the listing alone.

The remaining three cover states navigation cannot reach: Empty (an empty prefix, not an empty product), AwkwardNames (a 100-character filename beside a 1-byte and a 4-terabyte file), and Virtualized (250 rows, past MAX_VISIBLE_ITEMS).

The two static stories an earlier revision had are gone — the browsable one opens on what Default showed, and one click reaches what InADirectory showed.

BreadcrumbNav

Six stories. The one that earns its place is Truncated: past four segments the path collapses to the first two, an ellipsis, and the last two, and a real product rarely hands you a path that deep on demand. Also the rule that the current directory renders as plain text but the same segment becomes a link once a fileName sits below it.

ObjectSummary — the object detail view

Six stories. Its two conditional rows are the point, and both are awkward to arrange in the app:

  • Cloud URI appears only with a resolved connection, and takes a different shape per provider — s3://bucket/… on S3, an https://account.blob.core.windows.net/… URL on Azure, and no row at all on GCS. All three confirmed in a browser.
  • Checksum appears only when the object carries a sha256, and brings the verifier with it. That verifier is live — pressing it fetches the object, which fails here. The button and its states are what the story shows, not a passing check.

One config line, and why it is needed

CONFIG.storage.endpoint reads NEXT_PUBLIC_S3_ENDPOINT with no fallback, so every Source URL rendered as undefined/cascadia-research/…. On a published page that reads as a bug in the product rather than a gap in the environment.

It goes through a Vite define rather than Storybook's own env: the Next builder inlines NEXT_PUBLIC_* at build time from the real environment and never consults env. I tried that first and the value stayed undefined.

Providers, not stubs

The real S3CredentialsProvider and UploadProvider wrap the DirectoryList stories through a decorator, because the rows read upload progress from context.

No credentials mock. Nothing here uploads, so no credential is ever minted. I added the mock, found it unused, and removed it.

Verification

tsc clean · jest 65 suites / 567 tests · next lint 0 errors · storybook build succeeds.

🤖 Generated with Claude Code

https://claude.ai/code/session_011mnsg8m1dXZ5z5ig5xMtkE

@vercel

vercel Bot commented Aug 28, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
source-coop-ui Ready Ready Preview Aug 28, 2026 1:22am
source-cooperative Ready Ready Preview Aug 28, 2026 1:22am

Request Review

@claude

claude Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Claude finished @alukach's task in 1m 23s —— View job


Reviewing PR #521

  • Gather context (diff, changed files, CLAUDE.md)
  • Review for correctness/security/best-practice issues
  • Ponytail pass for over-engineering
  • Post final review

✅ No blocking issues — safe to merge.

Traced the pieces this PR depends on to confirm the story fixtures actually match production behavior:

  • BreadcrumbNav.tsx builds segment links as ${baseUrl}/${path.slice(0, i+1).join("/")} (no trailing slash) and the root link as baseUrl || "/" — matches the Browsable story's assumption that a breadcrumb href never ends in /.
  • DirectoryRow.tsx's href is objectUrl(...), which appends item.path verbatim — directories carry a trailing slash from asFileNodes, matching the story's assumption that row hrefs do.
  • ObjectSummary.tsx's checksum row requires both objectInfo.metadata.sha256 and product.account — the WithChecksum story sets both; DataProvider.S3/Azure/GCS string values ("s3", "azure", "gcs") line up with the cloudUri branching.
  • NEXT_PUBLIC_S3_ENDPOINT is genuinely read with no fallback in src/lib/config.ts:14, so the .storybook/main.ts viteFinal define is a real, not speculative, fix — value matches .env.example.

No correctness, security, or best-practice issues found in the diff.

Simplify (ponytail): none — BrowsableTree's prefix-in-state click intercept is already flagged in-code as a deliberate stand-in for routing (not a permanent abstraction), and the fixture helpers (object, childrenOf) are the minimum needed to drive the stories.


💰 Estimated review cost: $0.43 · 1m23s · 25 turns

`logging.ts` ends with a debug line whose `context` was `__filename` -- a
CommonJS global -- and that line runs on import. Any browser bundle
reaching this module threw `ReferenceError: __filename is not defined`
before rendering anything.

The field is a debug label naming where the log came from, so a literal
does the job.

This is the actual cause of the render failures worked around in #514,
#517 and #520. I had attributed them to server actions dragging the AWS
SDK into the browser; the actions were only one of several routes to this
module, and the barrels were others. Confirmed by disabling every
sb.mock() and rebuilding: DataConnectionForm renders in full without any
of them.

Worth considering separately: an ESLint rule banning __filename and
__dirname outside scripts/ would have caught this, and would catch the
next one.

tsc clean · jest 65 suites / 567 tests · next lint 0 errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011mnsg8m1dXZ5z5ig5xMtkE
Five stories for DirectoryList: the root of a product, one directory
down, an empty prefix, awkward names, and 250 rows through the
virtualizer. Every one of them otherwise needs real objects behind the
data proxy to look at.

The real S3CredentialsProvider and UploadProvider wrap each story through
a decorator, because the rows read upload progress from context. They are
the real providers, not stubs, and no credential is ever minted -- nothing
here uploads -- so no `credentials` mock is needed. I added one, found it
unused, and removed it again.

Stacked on the __filename fix, which is what made any of this renderable.

tsc clean · jest 65 suites / 567 tests · next lint 0 errors · storybook
build succeeds. Stories opened and confirmed rendering, including the
4 TB / 1 B size formatting and the truncated 100-character filename.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011mnsg8m1dXZ5z5ig5xMtkE
@alukach
alukach force-pushed the stories/object-browser branch from 0c87565 to 6f9bed9 Compare August 28, 2026 00:46
@alukach alukach changed the title fix(logging): stop LOGGER using __filename, and story the file tree docs(storybook): story the product file tree Aug 28, 2026
@alukach
alukach changed the base branch from main to fix/logger-filename August 28, 2026 00:46
@source-release-bot source-release-bot Bot added docs and removed fix labels Aug 28, 2026
Base automatically changed from fix/logger-filename to main August 28, 2026 00:49
alukach added a commit that referenced this pull request Aug 28, 2026
`src/lib/logging.ts` ends with a module-scope debug call:

```ts
LOGGER.debug("Loaded Config", { operation: "config", context: __filename, ... });
```

`__filename` is a **CommonJS global**, and this line runs on import. Any
browser bundle that reached this module threw `ReferenceError:
__filename is not defined` before rendering a single element.

The field is just a debug label naming where the log came from, so a
literal does the job. One line.

## This is the cause of the crashes worked around in #514, #517 and #520

I had attributed those to server actions dragging the AWS SDK into the
browser. **That was wrong.** The actions were only one of several routes
to `logging.ts`; the `@/lib` and `@/components/core` barrels were
others. The AWS SDK was never the problem.

Verified rather than assumed: with **every** `sb.mock()` disabled,
`DataConnectionForm` renders in full.

## What that means for the mocks already merged

They still earn their place — a story submitting a form would otherwise
invoke a real server action, and `searchAccounts` returning fixtures is
the only way the account picker's suggestion list draws. But *"these
components cannot render without mocks"* was not a true reason, and the
mock list can likely be trimmed. That deserves its own change rather
than being folded in here.

## Worth considering separately

An ESLint rule banning `__filename` / `__dirname` outside `scripts/`
would have caught this, and would catch the next one.

## Verification

`tsc` clean · jest 65 suites / 567 tests · `next lint` 0 errors.

The Storybook stories that this unblocks are stacked in #521.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_011mnsg8m1dXZ5z5ig5xMtkE

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
BreadcrumbNav gets six stories. The one worth having is Truncated: past
four segments it collapses to the first two, an ellipsis, and the last
two, and a real product rarely hands you a path that deep on demand.
Also the rule that the current directory is plain text while the same
segment becomes a link once a file sits below it.

DirectoryList gets a Browsable story that actually navigates. In the app
a directory is a <Link> and the server re-fetches the listing for the new
URL; Storybook has neither, so clicks went nowhere and each story was a
single frozen level. This holds the prefix in state, derives the listing
from one fixture tree, and intercepts clicks on links into the product.
That is a stand-in for the routing rather than the routing itself, and
the story says so -- but going down and back up with the breadcrumb
keeping pace is the thing worth reviewing, and it is reviewable now.

Verified in a browser rather than assumed: root -> recordings -> 2019
shows the two recordings, the breadcrumb walks back up through
recordings to root, and clicking a file leaves the listing alone. The
first attempt failed on a slash -- a row's href ends in "/" where a
breadcrumb's does not -- which the guard now normalises away.

tsc clean · jest 65 suites / 567 tests · next lint 0 errors · storybook
build succeeds.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011mnsg8m1dXZ5z5ig5xMtkE
@alukach alukach changed the title docs(storybook): story the product file tree docs(storybook): story the file tree, breadcrumbs, and browsing between them Aug 28, 2026
…il view

Browsable becomes Default and moves to the top of the file, since a
listing you cannot move around in is half the component. The two static
stories it supersedes are gone: its opening state was the old Default,
and one click reaches what InADirectory showed. Empty, AwkwardNames and
Virtualized stay -- those are states navigation cannot reach.

ObjectSummary gets six stories. Its two conditional rows are the point,
and both are awkward to arrange in the app: Cloud URI appears only with a
resolved connection and takes a different shape per provider -- s3:// on
S3, an HTTPS blob URL on Azure, absent on GCS -- and Checksum appears only
when the object carries a sha256, bringing the verifier with it.
Confirmed all three provider branches render as described, including GCS
having no Cloud URI row at all rather than an empty one.

Also sets NEXT_PUBLIC_S3_ENDPOINT for the Storybook build. CONFIG reads it
with no fallback, so every Source URL rendered as
"undefined/cascadia-research/..." -- which on a published page reads as a
bug in the product rather than a gap in the environment. It goes through
a vite `define`: Storybook's own `env` does not reach it, because the Next
builder inlines NEXT_PUBLIC_* at build time from the real environment.

tsc clean · jest 65 suites / 567 tests · next lint 0 errors · storybook
build succeeds. Default verified still navigating after the promotion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011mnsg8m1dXZ5z5ig5xMtkE
Storybook title-cases an export name to label the story, which turned
OnS3 into "On S 3". An explicit `name` fixes the label; the story id stays
as it was, so existing links keep working.

Azure and Google Cloud get one too, for consistency rather than because
they were broken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011mnsg8m1dXZ5z5ig5xMtkE
@alukach alukach changed the title docs(storybook): story the file tree, breadcrumbs, and browsing between them docs(storybook): story the object browser — file tree, breadcrumbs, and object detail Aug 28, 2026
@alukach
alukach merged commit 02a1de6 into main Aug 28, 2026
8 checks passed
@alukach
alukach deleted the stories/object-browser branch August 28, 2026 01:24
alukach added a commit that referenced this pull request Aug 28, 2026
Two conflicts, both in stories main had also touched:

- InviteMemberForm: main dropped the product-scoped story and its fixture
  (it rendered identically to the org one), so take main's side.
- ProductCreationForm: main gave the archive connection an owner so it
  filters on owner change (#522); keep that and rename it to miskatonic.

Main also added stories that carried the old names — the object browser
(#521) and BreadcrumbNav — so the same mapping is applied to them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant