Skip to content

Core: Scope localStorage per SB instance and purge stale tag filters#34195

Closed
Copilot wants to merge 15 commits into
nextfrom
copilot/fix-localstorage-persistence-issue
Closed

Core: Scope localStorage per SB instance and purge stale tag filters#34195
Copilot wants to merge 15 commits into
nextfrom
copilot/fix-localstorage-persistence-issue

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 18, 2026

  • Scope localStorage per Storybook instance using getAnonymousProjectId()
  • Purge stale tag filters after initial index load
  • Use anonymous fallback for non-Git projects
  • Persist Storybook version in localStorage for future migrations
  • Export getAnonymousProjectId from storybook/internal/telemetry
  • JSDoc comment on STORYBOOK_ANONYMOUS_PROJECT_ID assignment
  • Better variable names (allExistingTags, isExistingTag) in purge logic
  • Simplified storage key construction (global already has anonymous fallback)
  • Split store key test into two: mock telemetry to return an ID vs. undefined
  • Only inject anonymous project ID in dev mode (not built instances)
  • Restore storage migration logic (copy old shared key data to instance-scoped key)
  • Add unit test for migration logic
  • Deduplicate lastViewedStoryIds storage per instance
  • Fix merge conflicts with next branch (status filters, .ts extensions, URL-based tag persistence)

💬 Send tasks to Copilot coding agent from Slack and Teams to turn conversations into code. Copilot posts an update in your thread when it's finished.

Copilot AI changed the title [WIP] [Bug] Fix manager API localStorage persistence across instances Scope Manager API localStorage per Storybook instance; purge stale tag filters Mar 18, 2026
Copilot AI requested a review from Sidnioulz March 18, 2026 11:37
Comment thread code/core/src/manager-api/store.ts
Copy link
Copy Markdown
Contributor

@Sidnioulz Sidnioulz left a comment

Choose a reason for hiding this comment

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

@copilot address all code comments

Comment thread code/core/src/builder-manager/utils/framework.ts Outdated
Comment thread code/core/src/manager-api/tests/stories.test.ts
Comment thread code/core/src/manager-api/store.ts
Comment thread code/core/src/manager-api/store.ts
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 18, 2026

@copilot address all code comments

All comments addressed in d2f4089:

  • getAnonymousProjectId() instead of configDir hash
  • anonymous fallback for new data isolation
  • Version tracking in localStorage
  • Migration logic removed from get()
  • wait(16) replaced with vi.waitFor

Copilot AI requested a review from Sidnioulz March 18, 2026 18:15
Comment thread code/core/src/builder-manager/utils/framework.ts Outdated
Comment thread code/core/src/manager-api/modules/stories.ts Outdated
Comment thread code/core/src/manager-api/store.ts Outdated
Comment thread code/core/src/manager-api/tests/store.test.js Outdated
@Sidnioulz
Copy link
Copy Markdown
Contributor

Tested locally and the deduplication by ID works:
image

I also added fake keys to the storage, reloaded Storybook, and those keys were correctly removed from the store state.

@Sidnioulz Sidnioulz marked this pull request as ready for review March 20, 2026 16:55
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 20, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Added a helper function to purge stale tag filters after fetching the story index, removing filters that no longer match any tags unless they are built-in filters. Made storage keys instance-scoped by appending the anonymous project ID to prevent cross-project state collision. Added version tracking for persisted state by storing and checking a version identifier with stored data.

Changes

Cohort / File(s) Summary
Tag Filter Cleanup
code/core/src/manager-api/modules/stories.ts, code/core/src/manager-api/tests/stories.test.ts
Added purgeStaleTagFilters helper invoked during initialization to remove tag filters that don't exist in the fetched index, preserving built-in filters. Includes three test cases validating stale filter removal, built-in filter preservation, and cases where no update is needed.
Storage Instance Scoping & Version Tracking
code/core/src/manager-api/store.ts, code/core/src/manager-api/tests/store.test.js
Made STORAGE_KEY instance-specific by appending project ID via getAnonymousProjectId(). Added VERSION_KEY and persistVersion() to track persisted data versions. Updated getInitialState() to persist version metadata. Tests verify storage key scoping and version persistence behavior.
Telemetry Export
code/core/src/telemetry/index.ts
Re-exported getAnonymousProjectId from ./anonymous-id module to make it available for storage scoping logic.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • storybookjs/storybook#32526: Modifies tag-filtering behavior and built-in tag filter handling logic, directly related to the purgeStaleTagFilters feature.
  • storybookjs/storybook#32490: Alters types and internal traversal helpers in code/core/src/manager-api/modules/stories.ts, affecting the same module where tag filter cleanup is added.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
code/core/src/manager-api/typings.d.ts (1)

13-13: Consider adding JSDoc for consistency.

The corresponding declaration in code/core/src/manager/typings.d.ts includes a JSDoc comment explaining the purpose and fallback behavior. Consider adding the same documentation here for consistency across typing files.

📝 Suggested JSDoc addition
+/**
+ * A stable, anonymous project identifier derived from Git info (remote URL + working directory
+ * path). Used to scope localStorage keys per Storybook instance so that persistence is not shared
+ * across different projects. Falls back to 'anonymous' when Git info is unavailable.
+ */
 declare var STORYBOOK_INSTANCE_ID: string | undefined;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@code/core/src/manager-api/typings.d.ts` at line 13, Add a JSDoc comment above
the STORYBOOK_INSTANCE_ID declaration to match the other typings: document that
STORYBOOK_INSTANCE_ID may be undefined, explain its purpose as an instance
identifier and the fallback behavior (e.g., unused when undefined), and mirror
the wording used in the corresponding manager/typings.d.ts so both files stay
consistent; update the declaration for STORYBOOK_INSTANCE_ID to keep the same
type (string | undefined) without changing semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@code/core/src/manager-api/typings.d.ts`:
- Line 13: Add a JSDoc comment above the STORYBOOK_INSTANCE_ID declaration to
match the other typings: document that STORYBOOK_INSTANCE_ID may be undefined,
explain its purpose as an instance identifier and the fallback behavior (e.g.,
unused when undefined), and mirror the wording used in the corresponding
manager/typings.d.ts so both files stay consistent; update the declaration for
STORYBOOK_INSTANCE_ID to keep the same type (string | undefined) without
changing semantics.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 537677c2-6e87-44df-b889-204f954fcfa1

📥 Commits

Reviewing files that changed from the base of the PR and between 87733f1 and d6710e2.

📒 Files selected for processing (8)
  • code/core/src/builder-manager/utils/framework.ts
  • code/core/src/manager-api/modules/stories.ts
  • code/core/src/manager-api/store.ts
  • code/core/src/manager-api/tests/store.test.js
  • code/core/src/manager-api/tests/stories.test.ts
  • code/core/src/manager-api/typings.d.ts
  • code/core/src/manager/typings.d.ts
  • code/core/src/telemetry/index.ts

Comment thread code/core/src/manager-api/tests/store.test.js Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@code/core/src/manager-api/store.ts`:
- Around line 91-93: The call to persistVersion(store.local) writes to
localStorage even when persistence is disabled; update the initialization in the
store setup to check the allowPersistence flag (the setting that disables
localStorage persistence) and only call persistVersion(store.local) when
allowPersistence is true (or equivalent getter) so no persistence occurs when
allowPersistence is false; locate the call to persistVersion and guard it with
the allowPersistence condition used elsewhere in this module.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e411e8d1-3ed5-408c-9d99-b53f8859484f

📥 Commits

Reviewing files that changed from the base of the PR and between d6710e2 and 3d6fbef.

📒 Files selected for processing (3)
  • code/core/src/builder-manager/utils/framework.ts
  • code/core/src/manager-api/store.ts
  • code/core/src/telemetry/anonymous-id.test.ts
✅ Files skipped from review due to trivial changes (2)
  • code/core/src/telemetry/anonymous-id.test.ts
  • code/core/src/builder-manager/utils/framework.ts

Comment thread code/core/src/manager-api/store.ts
@Sidnioulz
Copy link
Copy Markdown
Contributor

For context, this PR addresses a general bug in our storage management which also affects keyboard shortcuts, layout options, etc. We never thought to deduplicate it per instance. It also addresses an issue specifically with stored tags which would occur with both sessionStorage or localStorage tags.

Comment thread code/core/src/manager-api/store.ts Outdated
Comment on lines -33 to -43
function get(storage: StoreAPI) {
let data = storage.get(STORAGE_KEY);

// Migration: if no data exists under the instance-specific key, try the old shared key.
// This is a one-time migration that preserves existing settings when upgrading.
if (!data && STORAGE_KEY !== STORAGE_KEY_BASE) {
data = storage.get(STORAGE_KEY_BASE);
if (data) {
storage.set(STORAGE_KEY, data);
}
}
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.

Readd this

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Re-added the migration logic in get() that copies data from the old shared key (@storybook/manager/store) to the instance-scoped key. Also added a unit test for it. See d5ae983.

Comment thread code/core/src/manager-api/store.ts
@Sidnioulz Sidnioulz requested review from valentinpalkovic and removed request for huang-julien and ndelangen March 26, 2026 08:45
@Sidnioulz Sidnioulz assigned Sidnioulz and unassigned ndelangen and huang-julien Mar 26, 2026
Copy link
Copy Markdown
Contributor

@Sidnioulz Sidnioulz left a comment

Choose a reason for hiding this comment

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

  • Make sure instance deduplication only happens when running dev, not on built instances where we might not want to even share the anonymous project id publicly
  • Restore the storage migration logic and, if possible, add a unit test for it
  • Also add the deduplication logic to lastViewedStoryIds

@copilot

Co-authored-by: Sidnioulz <5108577+Sidnioulz@users.noreply.github.com>
Agent-Logs-Url: https://github.com/storybookjs/storybook/sessions/1172a9ba-085a-4149-916f-63bf6a11d4d3
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 26, 2026

  • Make sure instance deduplication only happens when running dev, not on built instances where we might not want to even share the anonymous project id publicly
  • Restore the storage migration logic a...

All three items addressed in d5ae983:

  • STORYBOOK_ANONYMOUS_PROJECT_ID is now only injected when configType === 'DEVELOPMENT'; built instances won't expose the project ID
  • Migration logic restored in get() with a new unit test verifying old shared-key data is copied to the scoped key
  • lastViewedStoryIds now uses @storybook/manager/lastViewedStoryIds/{projectId} for per-instance isolation

Copy link
Copy Markdown
Contributor

@Sidnioulz Sidnioulz left a comment

Choose a reason for hiding this comment

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

@copilot fix conflicts. in case of doubt, prioritise the logic/spirit of the changes brought in by my PR, with the current code structure/architecture found on next, and let the human reviewer bring up concerns.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 2, 2026

@copilot fix conflicts. in case of doubt, prioritise the logic/spirit of the changes brought in by my PR, with the current code structure/architecture found on next, and let the human reviewer bring...

Conflicts resolved in 469ca5c. Key decisions:

  • .ts extensions adopted from next throughout
  • purgeStaleTagFilters now uses persistFilters (URL persistence) instead of { persistence: 'permanent' } to match the new tag filter URL-based storage on next
  • Both purgeStaleTagFilters and recomputeStatusFilter kept
  • All new status filter tests from next preserved alongside the existing purgeStaleTagFilters tests
  • All 116 tests pass (102 stories + 14 store)

Copilot AI requested a review from Sidnioulz April 2, 2026 11:46
@Sidnioulz Sidnioulz force-pushed the copilot/fix-localstorage-persistence-issue branch from 469ca5c to 2da328e Compare April 2, 2026 12:15
@Sidnioulz Sidnioulz closed this Apr 2, 2026
@Sidnioulz
Copy link
Copy Markdown
Contributor

I don't have time to continue this. If this is affecting you, feel free to pick it up! It's probably best to remove anything merge-rebase related as those branch syncs messed the branch up.

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

Labels

bug ci:normal upgrade:10.3 Issues/PRs found during 10.3 upgrade QA and post-release regressions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants