Skip to content

chore: Enable Storybook web for storybook-react-native#1134

Merged
georgewrmarshall merged 11 commits into
mainfrom
codex/storybook-react-native-backgrounds
Jun 5, 2026
Merged

chore: Enable Storybook web for storybook-react-native#1134
georgewrmarshall merged 11 commits into
mainfrom
codex/storybook-react-native-backgrounds

Conversation

@georgewrmarshall

@georgewrmarshall georgewrmarshall commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Description

Adds Storybook web support for apps/storybook-react-native and fixes the runtime issues needed to render the React Native component stories in the browser.

This PR:

  • adds a dedicated web Storybook setup for the React Native Storybook app
  • fixes multiple RN web rendering/runtime issues in shared components and stories
  • fills the Storybook iframe with the design-system themed canvas
  • fixes BottomSheet Storybook context requirements on web
  • aligns Storybook backgrounds across native and web using shared background/default token-backed definitions

Related issues

Part of: #87

Manual testing steps

  1. Run yarn storybook:web
  2. Open http://localhost:6006/
  3. Verify stories such as Examples/Backgrounds, Components/BannerAlert, and Components/BottomSheet render without the earlier web runtime errors

Screenshots/Recordings

Before

  • Storybook web never worked

After

  • Storybook react, ios and react native web all work
storybook.react.native.web.mov

Pre-merge author checklist

  • I've followed MetaMask Contributor Docs
  • I've completed the PR template to the best of my ability
  • I’ve included tests if applicable
  • I’ve documented my code using JSDoc format if applicable
  • I’ve applied the right labels on the PR (see labeling guidelines). Not required for external contributors.

Pre-merge reviewer checklist

  • I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed).
  • I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots.

Note

Medium Risk
Touches build tooling, Vite optimizeDeps/Babel for worklets, and patched node_modules—failures would block Storybook web/dev builds, not shipped product UI.

Overview
Adds browser Storybook for storybook-react-native via a new .storybook/ stack (@storybook/react-native-web-vite, react-native-web, storybook:web / build-storybook scripts) and a Vite config that aliases workspace design packages, serves fonts/SVGs, and runs the react-native-worklets Babel plugin during Rolldown optimizeDeps so Reanimated/worklets pre-bundle correctly on web.

Web preview wires design-token CSS, fullscreen layout, token-backed background globals (synced to ThemeProvider), and SafeAreaProvider / gesture-handler roots; the on-device preview gets the same safe-area wrapper and default background colors from tokens.

Yarn patches fix ESM resolution in @storybook/addon-ondevice-controls (.js import suffixes) and worklets source-map reads when Vite adds ?v= query strings. Storybook on-device packages bump to 10.4.4; ESLint/tsconfig include the new .storybook paths; README marks web Storybook as supported.

Reviewed by Cursor Bugbot for commit 3ad43a7. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@socket-security

socket-security Bot commented Apr 29, 2026

Copy link
Copy Markdown

All alerts resolved. Learn more about Socket for GitHub.

This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored.

Ignoring alerts on:

  • ua-parser-js@1.0.39
  • react-native-web@0.21.2
  • cross-fetch@3.1.8
  • node-fetch@2.6.7
  • @babel/core@7.29.0

View full report

@georgewrmarshall georgewrmarshall changed the title [codex] Enable Storybook web for storybook-react-native chore: Enable Storybook web for storybook-react-native May 21, 2026
@georgewrmarshall georgewrmarshall force-pushed the codex/storybook-react-native-backgrounds branch from 86941c2 to ec088b9 Compare May 21, 2026 23:31
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Dropped TextColor import causes runtime crash in stories
    • Added TextColor to the ../../Text imports in Blockies, Jazzicon, and Maskicon stories to prevent the runtime ReferenceError.

Create PR

Or push these changes by commenting:

@cursor push a17b7fa1e0
Preview (a17b7fa1e0)
diff --git a/packages/design-system-react-native/src/components/temp-components/Blockies/Blockies.stories.tsx b/packages/design-system-react-native/src/components/temp-components/Blockies/Blockies.stories.tsx
--- a/packages/design-system-react-native/src/components/temp-components/Blockies/Blockies.stories.tsx
+++ b/packages/design-system-react-native/src/components/temp-components/Blockies/Blockies.stories.tsx
@@ -7,7 +7,7 @@
 import { ScrollView } from 'react-native';
 
 import { Box } from '../../Box';
-import { Text, TextVariant, FontWeight } from '../../Text';
+import { Text, TextColor, TextVariant, FontWeight } from '../../Text';
 
 import { Blockies } from './Blockies';
 import type { BlockiesProps } from './Blockies.types';

diff --git a/packages/design-system-react-native/src/components/temp-components/Jazzicon/Jazzicon.stories.tsx b/packages/design-system-react-native/src/components/temp-components/Jazzicon/Jazzicon.stories.tsx
--- a/packages/design-system-react-native/src/components/temp-components/Jazzicon/Jazzicon.stories.tsx
+++ b/packages/design-system-react-native/src/components/temp-components/Jazzicon/Jazzicon.stories.tsx
@@ -7,7 +7,7 @@
 import { ScrollView } from 'react-native';
 
 import { Box } from '../../Box';
-import { Text, TextVariant, FontWeight } from '../../Text';
+import { Text, TextColor, TextVariant, FontWeight } from '../../Text';
 
 import { Jazzicon } from './Jazzicon';
 import type { JazziconProps } from './Jazzicon.types';

diff --git a/packages/design-system-react-native/src/components/temp-components/Maskicon/Maskicon.stories.tsx b/packages/design-system-react-native/src/components/temp-components/Maskicon/Maskicon.stories.tsx
--- a/packages/design-system-react-native/src/components/temp-components/Maskicon/Maskicon.stories.tsx
+++ b/packages/design-system-react-native/src/components/temp-components/Maskicon/Maskicon.stories.tsx
@@ -7,7 +7,7 @@
 import { ScrollView } from 'react-native';
 
 import { Box } from '../../Box';
-import { Text, TextVariant, FontWeight } from '../../Text';
+import { Text, TextColor, TextVariant, FontWeight } from '../../Text';
 
 import { Maskicon } from './Maskicon';
 import type { MaskiconProps } from './Maskicon.types';

You can send follow-ups to the cloud agent here.

@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Interpolate mock returns wrong value for out-of-range input
    • Updated interpolate to return the last output when value exceeds the input range (i === -1) while preserving first-output behavior for i === 0.

Create PR

Or push these changes by commenting:

@cursor push 3c902a14bd
Preview (3c902a14bd)
diff --git a/apps/storybook-react-native/.storybook/reanimated-mock.ts b/apps/storybook-react-native/.storybook/reanimated-mock.ts
--- a/apps/storybook-react-native/.storybook/reanimated-mock.ts
+++ b/apps/storybook-react-native/.storybook/reanimated-mock.ts
@@ -94,8 +94,8 @@
   outputRange: number[],
 ): number => {
   const i = inputRange.findIndex((v) => v >= value);
-  if (i <= 0) return outputRange[0];
-  if (i >= inputRange.length) return outputRange[outputRange.length - 1];
+  if (i === 0) return outputRange[0];
+  if (i === -1) return outputRange[outputRange.length - 1];
   const t = (value - inputRange[i - 1]) / (inputRange[i] - inputRange[i - 1]);
   return outputRange[i - 1] + t * (outputRange[i] - outputRange[i - 1]);
 };

You can send follow-ups to the cloud agent here.

Comment thread apps/storybook-react-native/.storybook/reanimated-mock.ts Outdated
@georgewrmarshall georgewrmarshall self-assigned this May 22, 2026
<GestureHandlerRootView style={{ flex: 1 }}>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
<GestureHandlerRootView style={{ flex: 1, backgroundColor }}>
<SafeAreaProvider>

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.

SafeAreaProvider is required because several components — most notably BottomSheet and BottomSheetDialog — call useSafeAreaInsets() internally to calculate bottom padding for home indicator clearance. Without a provider in the tree, the hook returns zero insets and the sheet renders flush to the screen edge, which doesn't match production. In a real app the provider lives at the root; this replicates that context for all stories.

return (
<GestureHandlerRootView style={{ flex: 1 }}>
<ThemeProvider theme={theme}>{children}</ThemeProvider>
<GestureHandlerRootView style={{ flex: 1, backgroundColor }}>

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.

The canvas background is set to the background.default design token value rather than Storybook's default white. Components like Box with bg-default or bg-alternative use the same token as their surface colour — rendering them on white would make them invisible or misleading. The raw hex is read directly from @metamask/design-tokens rather than resolved through TWRNC because React Native's style prop requires an actual colour string, not a Tailwind class.

* Each @font-face must use the matching PostScript name so the browser resolves it.
*/

@font-face { font-family: 'Geist-Regular'; src: url('/fonts/Geist/Geist-Regular.woff2') format('woff2'); }

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.

Browsers normally resolve font variants by matching a single font-family name against font-weight and font-style descriptors — e.g. font-family: 'Geist'; font-weight: 500 would resolve to the Medium variant. react-native-web bypasses this entirely: it passes the fontFamily value computed by TWRNC directly as a CSS font-family inline style, so font-default-medium becomes font-family: 'Geist-Medium' in the browser. A conventionally-named @font-face block would never match. Each declaration here must use the PostScript name as its font-family value so the browser can resolve it.

@font-face { font-family: 'Geist-SemiBold'; src: url('/fonts/Geist/Geist-SemiBold.woff2') format('woff2'); }
@font-face { font-family: 'Geist-SemiBoldItalic'; src: url('/fonts/Geist/Geist-SemiBoldItalic.woff2') format('woff2'); }

@font-face { font-family: 'MMSans-Regular'; src: url('/fonts/MMSans/MMSans-Regular.woff2') format('woff2'); }

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.

woff2 is used here rather than the OTF files in the fonts/ directory because woff2 is the browser-native format — it's compressed and doesn't require any additional loader. The OTF files exist for the native Expo Go runtime (useFonts in index.tsx); the woff2 files in public/fonts/ are the web equivalent served by the Storybook static server.

@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 3 total unresolved issues (including 2 from previous reviews).

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Native font embedding removed from app.json plugins
    • Re-added the expo-font plugin to apps/storybook-react-native/app.json and listed all native font files so iOS/Android builds bundle and register Geist, MMSans, and MMPoly.

Create PR

Or push these changes by commenting:

@cursor push 213509e6aa
Preview (213509e6aa)
diff --git a/apps/storybook-react-native/app.json b/apps/storybook-react-native/app.json
--- a/apps/storybook-react-native/app.json
+++ b/apps/storybook-react-native/app.json
@@ -16,6 +16,24 @@
         "backgroundColor": "#ffffff"
       }
     },
-    "plugins": []
+    "plugins": [
+      [
+        "expo-font",
+        {
+          "fonts": [
+            "./fonts/Geist/Geist-Regular.otf",
+            "./fonts/Geist/Geist-RegularItalic.otf",
+            "./fonts/Geist/Geist-Medium.otf",
+            "./fonts/Geist/Geist-MediumItalic.otf",
+            "./fonts/Geist/Geist-SemiBold.otf",
+            "./fonts/Geist/Geist-SemiBoldItalic.otf",
+            "./fonts/MMSans/MMSans-Regular.otf",
+            "./fonts/MMSans/MMSans-Medium.otf",
+            "./fonts/MMSans/MMSans-Bold.otf",
+            "./fonts/MMPoly/MMPoly-Regular.otf"
+          ]
+        }
+      ]
+    ]
   }
 }

You can send follow-ups to the cloud agent here.

Comment thread apps/storybook-react-native/app.json
@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@github-actions

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

Comment thread packages/design-system-react-native/src/components/Icon/Icon.tsx
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Vite 8 upgrade breaks rollupOptions in storybook-react config
    • Replaced build.rollupOptions with build.rolldownOptions in apps/storybook-react/vite.config.ts to keep .md/.mdx externalization working on Vite 8 and pushed the change.

Create PR

Or push these changes by commenting:

@cursor push 5720eaeb04
Preview (5720eaeb04)
diff --git a/apps/storybook-react/vite.config.ts b/apps/storybook-react/vite.config.ts
--- a/apps/storybook-react/vite.config.ts
+++ b/apps/storybook-react/vite.config.ts
@@ -18,7 +18,7 @@
   },
   assetsInclude: ['**/*.md', '**/*.mdx'],
   build: {
-    rollupOptions: {
+    rolldownOptions: {
       external: [/\.md$/, /\.mdx$/],
     },
   },

You can send follow-ups to the cloud agent here.

Comment thread apps/storybook-react/package.json
@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

inputMap.sourcesContent = [];
for (const sourceFile of inputMap.sources) {
- inputMap.sourcesContent.push(fs.readFileSync(sourceFile).toString("utf-8"));
+ inputMap.sourcesContent.push(fs.readFileSync(sourceFile.replace(/[?#].*$/, "")).toString("utf-8"));

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.

The worklets Babel plugin calls fs.readFileSync on source file paths when generating sourcemaps, but Vite appends ?v=abc123 cache-busting query strings to filenames before passing them to transform plugins. The file on disk has no such suffix, so the read throws ENOENT. Stripping any ? or # suffix with .replace(/[?#].*$/, "") before the read fixes this without affecting sourcemap content. This matches the upstream fix proposed in the react-native-reanimated fork and tracked in dannyhw/vite-plugin-rnw#13. The patch stays in place until react-native-worklets ships a release containing the fix.

-import NoControlsWarning from './NoControlsWarning';
-import PropForm from './PropForm';
-import { useArgs } from './hooks';
+import NoControlsWarning from './NoControlsWarning.js';

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.

@storybook/addon-ondevice-controls ships its dist/ as ESM but omits file extensions on all relative imports (e.g. './PropForm' instead of './PropForm.js'). Node.js's strict ESM resolver requires explicit extensions for relative imports, so every internal module fails to load at runtime in the on-device Storybook environment. The patch adds .js to every affected import throughout the dist. The issue is present in all published versions through 10.4.4 and has not been fixed upstream.

The worklets Babel plugin threw ENOENT when Vite appended ?v=… cache-
busting query strings to source file paths before passing them to
readFileSync. This blocked the modulesToTranspile approach from working
and forced us to alias react-native-reanimated to a no-op stub.

Patch react-native-worklets@0.5.1 (plugin/index.js line 697) to strip
?query and #hash suffixes before readFileSync — matching the upstream
fix proposed in NiGhTTraX:react-native-reanimated:fix/filename-querystring
and discussed in dannyhw/vite-plugin-rnw#13.

With the patch applied, vite-plugin-rnw's esbuild resolveExtensions
(which puts .web.js before .js) correctly selects index.web.js for
Reanimated's directory imports during pre-bundling, and the worklets
Babel plugin processes the pre-bundled file without error.

Remove reanimated-mock.ts and the alias — real Reanimated now runs in
web Storybook with full animation support.
…ugin in Rolldown dep phase

The Storybook preset's modulesToTranspile exclude regex matches the outer
`/node_modules/` segment in the Vite dep cache path
(.cache/storybook/.../sb-vite/deps/react-native-reanimated.js), so the
worklets Babel plugin never fires on the pre-bundled Reanimated file during
the transform phase. Without the worklets transformation, Reanimated's
module initialization fails and the default Animated export is undefined,
crashing any component that calls Animated.createAnimatedComponent.

Upgrade Vite to 8 so vite-plugin-rnw uses Rolldown for dep optimization,
then inject the worklets Babel plugin into optimizeDeps.rolldownOptions.plugins.
This runs the plugin on the original source files during pre-bundling —
before they are concatenated into the giant cache file — where the
exclude regex based on .* correctly identifies the packages by name.

Also add @rolldown/plugin-babel as a dev dependency.

Solution from: dannyhw/vite-plugin-rnw#13
…for web

Add position:relative to GestureHandlerRootView so absolute-positioned
components (BottomSheet, BottomSheetDialog, BottomSheetOverlay) resolve
their CSS positioning context correctly on web.

Add html/body/storybook-root height rules to fonts.css so the flex:1
chain propagates correctly through SafeAreaProvider, ensuring
useSafeAreaFrame() returns the actual canvas dimensions on first render.

Closes #1196
The file now contains both global layout rules (html/body/storybook-root
height chain) and font declarations, so fonts.css was no longer accurate.
@georgewrmarshall georgewrmarshall force-pushed the codex/storybook-react-native-backgrounds branch from 9e2aaaf to f4a33d5 Compare June 3, 2026 20:54
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Native preview missing background options
    • Added backgrounds options and initialGlobals to native preview and synced ThemeProvider to the selected globals.backgrounds key to match web.

Create PR

Or push these changes by commenting:

@cursor push 225051d005
Preview (225051d005)
diff --git a/apps/storybook-react-native/.rnstorybook/preview.tsx b/apps/storybook-react-native/.rnstorybook/preview.tsx
--- a/apps/storybook-react-native/.rnstorybook/preview.tsx
+++ b/apps/storybook-react-native/.rnstorybook/preview.tsx
@@ -2,13 +2,24 @@
 import { Theme, ThemeProvider } from '@metamask/design-system-twrnc-preset';
 import { darkTheme, lightTheme } from '@metamask/design-tokens';
 import React, { type PropsWithChildren } from 'react';
-import { useColorScheme } from 'react-native';
 import { GestureHandlerRootView } from 'react-native-gesture-handler';
 import { SafeAreaProvider } from 'react-native-safe-area-context';
 
-const ThemeDecorator = ({ children }: PropsWithChildren) => {
-  const colorScheme = useColorScheme();
-  const theme = colorScheme === 'dark' ? Theme.Dark : Theme.Light;
+// Background options keyed by name so on-device backgrounds toolbar
+// and context.globals.backgrounds.value (which returns the key) both work.
+const backgroundOptions = {
+  light: { name: 'light', value: lightTheme.colors.background.default },
+  dark: { name: 'dark', value: darkTheme.colors.background.default },
+};
+
+function themeFromKey(key?: string): Theme {
+  return key === 'dark' ? Theme.Dark : Theme.Light;
+}
+
+type ThemeDecoratorProps = PropsWithChildren<{ selectedKey?: string }>;
+
+const ThemeDecorator = ({ children, selectedKey }: ThemeDecoratorProps) => {
+  const theme = themeFromKey(selectedKey);
   const backgroundColor =
     theme === Theme.Dark
       ? darkTheme.colors.background.default
@@ -25,12 +36,26 @@
 
 const preview: Preview = {
   decorators: [
-    (Story: React.ComponentType) => (
-      <ThemeDecorator>
-        <Story />
-      </ThemeDecorator>
-    ),
+    (Story: React.ComponentType, context: any) => {
+      const selectedKey = context.globals?.backgrounds?.value as
+        | string
+        | undefined;
+      return (
+        <ThemeDecorator selectedKey={selectedKey}>
+          <Story />
+        </ThemeDecorator>
+      );
+    },
   ],
+  parameters: {
+    backgrounds: {
+      options: backgroundOptions,
+    },
+    layout: 'fullscreen',
+  },
+  initialGlobals: {
+    backgrounds: { value: 'light' },
+  },
 };
 
 export default preview;

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit f4a33d5. Configure here.

const preview: Preview = {
decorators: [
(Story) => (
(Story: React.ComponentType) => (

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.

Native preview missing background options

Medium Severity

This PR registers Storybook background options and syncs ThemeProvider to globals.backgrounds on web, but the native .rnstorybook/preview.tsx still exports only decorators. On-device backgrounds needs parameters.backgrounds.options in preview (as on web), and native theme still follows useColorScheme() instead of the selected background key, so canvas and theme can disagree.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f4a33d5. Configure here.

@georgewrmarshall georgewrmarshall Jun 3, 2026

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.

This breaks theming if we add backgrounds. I tried added but then intentionally removed

Comment thread apps/storybook-react-native/tsconfig.json Outdated
…rom tsconfig

Co-authored-by: George Marshall <georgewrmarshall@users.noreply.github.com>
@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@georgewrmarshall georgewrmarshall marked this pull request as ready for review June 3, 2026 21:02
@georgewrmarshall georgewrmarshall requested a review from a team as a code owner June 3, 2026 21:02
@georgewrmarshall georgewrmarshall enabled auto-merge (squash) June 5, 2026 16:05
@github-actions

github-actions Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

📖 Storybook Preview

@georgewrmarshall georgewrmarshall merged commit f8a1f4b into main Jun 5, 2026
36 checks passed
@georgewrmarshall georgewrmarshall deleted the codex/storybook-react-native-backgrounds branch June 5, 2026 16:09
georgewrmarshall added a commit that referenced this pull request Jun 9, 2026
…#1188)

## **Description**

Adds React Native component stories to the existing React Storybook by
using Storybook composition.

The React Storybook now acts as the host Storybook and includes a
composed **React Native Components** section. This makes the React
Native web Storybook available from the same Storybook UI in local
development, PR preview builds, and the deployed web Storybook.

Storybook composition lets one Storybook reference another Storybook by
URL so stories from both appear together in the sidebar, even when they
use different renderers or project setup:
https://storybook.js.org/docs/sharing/storybook-composition

## **What changed**

- Adds a `react-native` Storybook `refs` entry in
`apps/storybook-react/.storybook/main.ts`.
- Points the composed React Native Storybook to `http://localhost:6007`
during local development.
- Points the composed React Native Storybook to `./react-native` for
static hosted builds.
- Updates the main Storybook workflow to build the React Storybook and
React Native web Storybook, then nest the React Native static build
under `storybook-static/react-native/` before deploying to GitHub Pages.
- Updates the PR Storybook workflow to do the same nesting before
uploading PR preview builds, so React Native component stories are
available in PR builds too.

## **Local dev workflow**

```bash
# Terminal 1
yarn storybook:web

# Terminal 2
yarn storybook
```

Open `http://localhost:6006` and expand **React Native Components** in
the sidebar.

## **Related issues**

Depends on: #1134

## **Manual testing steps**

1. Start `yarn storybook:web` and wait for the React Native web
Storybook to run on port 6007.
2. Start `yarn storybook` and open `http://localhost:6006`.
3. Confirm **React Native Components** appears in the sidebar.
4. Expand **React Native Components** and open a story.
5. Confirm the story renders in the main canvas.
6. Open the PR Storybook preview build.
7. Confirm **React Native Components** is available there as well.

## **Screenshots/Recordings**

### **After**

The hosted React Storybook includes a composed **React Native
Components** section backed by the React Native web Storybook.


https://github.com/user-attachments/assets/ec3eb3d6-e368-482e-a090-801e51c67107

## **Pre-merge author checklist**

- [x] I've followed [MetaMask Contributor
Docs](https://github.com/MetaMask/contributor-docs)
- [x] I've completed the PR template to the best of my ability
- [x] I've included tests if applicable
- [x] I've documented my code using [JSDoc](https://jsdoc.app/) format
if applicable
- [ ] I've applied the right labels on the PR

## **Pre-merge reviewer checklist**

- [ ] I've manually tested the PR (e.g. pull and build branch, run the
app, test code being changed).
- [ ] I confirm that this PR addresses all acceptance criteria described
in the ticket it closes and includes the necessary testing evidence such
as recordings and or screenshots.
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.

3 participants