@storybook/react-native 10.3.0-next.1 → 10.3.0-next.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -103,6 +103,7 @@ declare function prepareStories({ storyEntries, options, storySort, }: {
103
103
  };
104
104
  importMap: Record<string, any>;
105
105
  };
106
+
106
107
  declare const getProjectAnnotations: (view: View, annotations: any[]) => () => Promise<storybook_internal_types.NormalizedProjectAnnotations<ReactRenderer>>;
107
108
  declare function start({ annotations, storyEntries, options, }: {
108
109
  storyEntries: (NormalizedStoriesSpecifier & {
package/dist/index.js CHANGED
@@ -98,10 +98,10 @@ var init_constants = __esm({
98
98
  });
99
99
 
100
100
  // src/backgrounds/BackgroundPanel.tsx
101
- var import_react_native4, import_react5, import_core_events2, import_react_native_theming4, import_jsx_runtime5, codeSample, ThemedText, TitleText, ParagraphText, LockedText, Instructions, BackgroundPanel, BackgroundPanel_default;
101
+ var import_react_native5, import_react5, import_core_events2, import_react_native_theming4, import_jsx_runtime5, codeSample, ThemedText, TitleText, ParagraphText, LockedText, Instructions, BackgroundPanel, BackgroundPanel_default;
102
102
  var init_BackgroundPanel = __esm({
103
103
  "src/backgrounds/BackgroundPanel.tsx"() {
104
- import_react_native4 = require("react-native");
104
+ import_react_native5 = require("react-native");
105
105
  import_react5 = require("react");
106
106
  import_core_events2 = require("storybook/internal/core-events");
107
107
  import_react_native_theming4 = require("@storybook/react-native-theming");
@@ -147,7 +147,7 @@ export default preview;
147
147
  fontStyle: "italic",
148
148
  opacity: 0.7
149
149
  }));
150
- Instructions = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native4.View, { children: [
150
+ Instructions = () => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { children: [
151
151
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TitleText, { children: "Setup Instructions" }),
152
152
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ParagraphText, { children: "Add background options to your preview parameters. Each option should include a name and the corresponding color value." }),
153
153
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(ParagraphText, { children: "Below is an example of how to configure backgrounds in your preview config. Long press the example to copy it." }),
@@ -155,8 +155,8 @@ export default preview;
155
155
  ] });
156
156
  BackgroundPanel = ({ active, api, channel }) => {
157
157
  const store2 = api.store();
158
- const storyId = store2.getSelection().storyId;
159
- const story = store2.fromId(storyId);
158
+ const storyId = store2.getSelection()?.storyId;
159
+ const story = storyId ? store2.fromId(storyId) : null;
160
160
  const isLocked = !!story?.storyGlobals?.[PARAM_KEY];
161
161
  const setBackground = (0, import_react5.useCallback)(
162
162
  (name) => {
@@ -164,15 +164,12 @@ export default preview;
164
164
  },
165
165
  [channel]
166
166
  );
167
- if (!active) {
168
- return null;
169
- }
170
- const bgParams = story.parameters[PARAM_KEY];
167
+ const bgParams = story?.parameters?.[PARAM_KEY];
171
168
  const options = bgParams?.options;
172
169
  if (options && Object.keys(options).length > 0) {
173
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native4.View, { style: { padding: 10 }, children: [
170
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_react_native5.View, { style: { padding: 10 }, children: [
174
171
  isLocked && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LockedText, { children: "Background is set at the story level" }),
175
- Object.entries(options).map(([key, { name, value }]) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native4.View, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
172
+ Object.entries(options).map(([key, { name, value }]) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.View, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
176
173
  Swatch_default,
177
174
  {
178
175
  value,
@@ -183,7 +180,7 @@ export default preview;
183
180
  ) }, `${key} ${value}`))
184
181
  ] });
185
182
  }
186
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native4.View, { style: { padding: 10 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Instructions, {}) });
183
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native5.View, { style: { padding: 10 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Instructions, {}) });
187
184
  };
188
185
  BackgroundPanel_default = BackgroundPanel;
189
186
  }
@@ -231,24 +228,9 @@ __export(index_exports, {
231
228
  module.exports = __toCommonJS(index_exports);
232
229
  var import_react_native_theming5 = require("@storybook/react-native-theming");
233
230
 
234
- // ../../node_modules/react-native-web/dist/exports/Platform/index.js
235
- var Platform = {
236
- OS: "web",
237
- select: (obj) => "web" in obj ? obj.web : obj.default,
238
- get isTesting() {
239
- if (process.env.NODE_ENV === "test") {
240
- return true;
241
- }
242
- return false;
243
- },
244
- get Version() {
245
- return "0.0.0";
246
- }
247
- };
248
- var Platform_default = Platform;
249
-
250
231
  // src/polyfill.ts
251
- if (Platform_default.OS !== "web") {
232
+ var import_react_native = require("react-native");
233
+ if (import_react_native.Platform.OS !== "web") {
252
234
  try {
253
235
  let params = new URLSearchParams({ test: "1" });
254
236
  params.get("test");
@@ -257,12 +239,14 @@ if (Platform_default.OS !== "web") {
257
239
  setupURLPolyfill();
258
240
  }
259
241
  }
242
+ if (import_react_native.Platform.OS === "web" && typeof globalThis.setImmediate === "undefined") {
243
+ require("setimmediate");
244
+ }
260
245
 
261
246
  // src/Start.tsx
262
- var import_react_native5 = require("react-native");
247
+ var import_react_native6 = require("react-native");
263
248
  var import_manager_api3 = require("storybook/manager-api");
264
- var import_preview_api2 = require("storybook/internal/preview-api");
265
- var import_csf2 = require("storybook/internal/csf");
249
+ var import_preview_api3 = require("storybook/internal/preview-api");
266
250
  var import_channels2 = require("storybook/internal/channels");
267
251
 
268
252
  // src/View.tsx
@@ -274,14 +258,63 @@ var import_core_events = require("storybook/internal/core-events");
274
258
  var import_manager_api = require("storybook/manager-api");
275
259
  var import_preview_api = require("storybook/internal/preview-api");
276
260
  var import_dedent = __toESM(require("dedent"));
261
+
262
+ // src/patchChannelForRN.ts
263
+ function snapshotValue(value, depth = 0) {
264
+ if (depth > 3) return "[...]";
265
+ if (value === null || value === void 0) return value;
266
+ if (typeof value === "function") return void 0;
267
+ if (typeof value !== "object") return value;
268
+ if (Array.isArray(value)) {
269
+ return value.map((item) => snapshotValue(item, depth + 1));
270
+ }
271
+ const result = {};
272
+ for (const key of Object.keys(value)) {
273
+ try {
274
+ result[key] = snapshotValue(value[key], depth + 1);
275
+ } catch {
276
+ result[key] = "[Error]";
277
+ }
278
+ }
279
+ return result;
280
+ }
281
+ function isSyntheticEvent(value) {
282
+ return value !== null && typeof value === "object" && typeof value.persist === "function" && "nativeEvent" in value;
283
+ }
284
+ function sanitizeActionArgs(value) {
285
+ if (isSyntheticEvent(value)) {
286
+ return snapshotValue(value);
287
+ }
288
+ if (Array.isArray(value)) {
289
+ return value.map(sanitizeActionArgs);
290
+ }
291
+ return value;
292
+ }
293
+ function patchChannelForRN(channel) {
294
+ globalThis.CHANNEL_OPTIONS = {
295
+ maxDepth: 5
296
+ };
297
+ const originalEmit = channel.emit.bind(channel);
298
+ channel.emit = (eventName, ...args) => {
299
+ if (eventName === "storybook/actions/action-event") {
300
+ const actionDisplay = args[0];
301
+ if (actionDisplay?.data?.args != null) {
302
+ actionDisplay.data.args = sanitizeActionArgs(actionDisplay.data.args);
303
+ }
304
+ }
305
+ return originalEmit(eventName, ...args);
306
+ };
307
+ }
308
+
309
+ // src/View.tsx
277
310
  var import_deepmerge = __toESM(require("deepmerge"));
278
311
  var import_react4 = require("react");
279
- var import_react_native3 = require("react-native");
312
+ var import_react_native4 = require("react-native");
280
313
 
281
314
  // src/components/StoryView/StoryView.tsx
282
315
  var import_react_native_theming = require("@storybook/react-native-theming");
283
316
  var import_react3 = __toESM(require("react"));
284
- var import_react_native2 = require("react-native");
317
+ var import_react_native3 = require("react-native");
285
318
 
286
319
  // ../../node_modules/jotai/esm/vanilla/internals.mjs
287
320
  var import_meta = {};
@@ -303,30 +336,6 @@ function returnAtomValue(atomState) {
303
336
  }
304
337
  return atomState.v;
305
338
  }
306
- var promiseStateMap = /* @__PURE__ */ new WeakMap();
307
- function isPendingPromise(value) {
308
- var _a;
309
- return isPromiseLike(value) && !!((_a = promiseStateMap.get(value)) == null ? void 0 : _a[0]);
310
- }
311
- function abortPromise(promise) {
312
- const promiseState = promiseStateMap.get(promise);
313
- if (promiseState == null ? void 0 : promiseState[0]) {
314
- promiseState[0] = false;
315
- promiseState[1].forEach((fn) => fn());
316
- }
317
- }
318
- function registerAbortHandler(promise, abortHandler) {
319
- let promiseState = promiseStateMap.get(promise);
320
- if (!promiseState) {
321
- promiseState = [true, /* @__PURE__ */ new Set()];
322
- promiseStateMap.set(promise, promiseState);
323
- const settle = () => {
324
- promiseState[0] = false;
325
- };
326
- promise.then(settle, settle);
327
- }
328
- promiseState[1].add(abortHandler);
329
- }
330
339
  function isPromiseLike(p) {
331
340
  return typeof (p == null ? void 0 : p.then) === "function";
332
341
  }
@@ -484,6 +493,7 @@ var BUILDING_BLOCK_readAtomState = (store2, atom2) => {
484
493
  const writeAtomState = buildingBlocks[16];
485
494
  const mountDependencies = buildingBlocks[17];
486
495
  const setAtomStateValueOrPromise = buildingBlocks[20];
496
+ const registerAbortHandler = buildingBlocks[26];
487
497
  const atomState = ensureAtomState(store2, atom2);
488
498
  if (isAtomStateInitialized(atomState)) {
489
499
  if (mountedMap.has(atom2) && invalidatedAtoms.get(atom2) !== atomState.n) {
@@ -500,16 +510,27 @@ var BUILDING_BLOCK_readAtomState = (store2, atom2) => {
500
510
  return atomState;
501
511
  }
502
512
  }
503
- atomState.d.clear();
504
513
  let isSync = true;
505
- function mountDependenciesIfAsync() {
514
+ const prevDeps = new Set(atomState.d.keys());
515
+ const nextDeps = /* @__PURE__ */ new Map();
516
+ const pruneDependencies = () => {
517
+ for (const a of prevDeps) {
518
+ if (!nextDeps.has(a)) {
519
+ atomState.d.delete(a);
520
+ }
521
+ }
522
+ };
523
+ const mountDependenciesIfAsync = () => {
506
524
  if (mountedMap.has(atom2)) {
525
+ const shouldRecompute = !changedAtoms.size;
507
526
  mountDependencies(store2, atom2);
508
- recomputeInvalidatedAtoms(store2);
509
- flushCallbacks(store2);
527
+ if (shouldRecompute) {
528
+ recomputeInvalidatedAtoms(store2);
529
+ flushCallbacks(store2);
530
+ }
510
531
  }
511
- }
512
- function getter(a) {
532
+ };
533
+ const getter = (a) => {
513
534
  var _a2;
514
535
  if (a === atom2) {
515
536
  const aState2 = ensureAtomState(store2, a);
@@ -526,8 +547,9 @@ var BUILDING_BLOCK_readAtomState = (store2, atom2) => {
526
547
  try {
527
548
  return returnAtomValue(aState);
528
549
  } finally {
550
+ nextDeps.set(a, aState.n);
529
551
  atomState.d.set(a, aState.n);
530
- if (isPendingPromise(atomState.v)) {
552
+ if (isPromiseLike(atomState.v)) {
531
553
  addPendingPromiseToDependency(atom2, atomState.v, aState);
532
554
  }
533
555
  if (mountedMap.has(atom2)) {
@@ -537,7 +559,7 @@ var BUILDING_BLOCK_readAtomState = (store2, atom2) => {
537
559
  mountDependenciesIfAsync();
538
560
  }
539
561
  }
540
- }
562
+ };
541
563
  let controller;
542
564
  let setSelf;
543
565
  const options = {
@@ -587,8 +609,14 @@ var BUILDING_BLOCK_readAtomState = (store2, atom2) => {
587
609
  }
588
610
  setAtomStateValueOrPromise(store2, atom2, valueOrPromise);
589
611
  if (isPromiseLike(valueOrPromise)) {
590
- registerAbortHandler(valueOrPromise, () => controller == null ? void 0 : controller.abort());
591
- valueOrPromise.then(mountDependenciesIfAsync, mountDependenciesIfAsync);
612
+ registerAbortHandler(store2, valueOrPromise, () => controller == null ? void 0 : controller.abort());
613
+ const settle = () => {
614
+ pruneDependencies();
615
+ mountDependenciesIfAsync();
616
+ };
617
+ valueOrPromise.then(settle, settle);
618
+ } else {
619
+ pruneDependencies();
592
620
  }
593
621
  (_a = storeHooks.r) == null ? void 0 : _a.call(storeHooks, atom2);
594
622
  return atomState;
@@ -617,8 +645,10 @@ var BUILDING_BLOCK_invalidateDependents = (store2, atom2) => {
617
645
  const aState = ensureAtomState(store2, a);
618
646
  for (const d of getMountedOrPendingDependents(a, aState, mountedMap)) {
619
647
  const dState = ensureAtomState(store2, d);
620
- invalidatedAtoms.set(d, dState.n);
621
- stack.push(d);
648
+ if (invalidatedAtoms.get(d) !== dState.n) {
649
+ invalidatedAtoms.set(d, dState.n);
650
+ stack.push(d);
651
+ }
622
652
  }
623
653
  }
624
654
  };
@@ -686,7 +716,7 @@ var BUILDING_BLOCK_mountDependencies = (store2, atom2) => {
686
716
  const unmountAtom = buildingBlocks[19];
687
717
  const atomState = ensureAtomState(store2, atom2);
688
718
  const mounted = mountedMap.get(atom2);
689
- if (mounted && !isPendingPromise(atomState.v)) {
719
+ if (mounted) {
690
720
  for (const [a, n] of atomState.d) {
691
721
  if (!mounted.d.has(a)) {
692
722
  const aState = ensureAtomState(store2, a);
@@ -807,7 +837,9 @@ var BUILDING_BLOCK_unmountAtom = (store2, atom2) => {
807
837
  return mounted;
808
838
  };
809
839
  var BUILDING_BLOCK_setAtomStateValueOrPromise = (store2, atom2, valueOrPromise) => {
810
- const ensureAtomState = getInternalBuildingBlocks(store2)[11];
840
+ const buildingBlocks = getInternalBuildingBlocks(store2);
841
+ const ensureAtomState = buildingBlocks[11];
842
+ const abortPromise = buildingBlocks[27];
811
843
  const atomState = ensureAtomState(store2, atom2);
812
844
  const hasPrevValue = "v" in atomState;
813
845
  const prevValue = atomState.v;
@@ -825,7 +857,7 @@ var BUILDING_BLOCK_setAtomStateValueOrPromise = (store2, atom2, valueOrPromise)
825
857
  if (!hasPrevValue || !Object.is(prevValue, atomState.v)) {
826
858
  ++atomState.n;
827
859
  if (isPromiseLike(prevValue)) {
828
- abortPromise(prevValue);
860
+ abortPromise(store2, prevValue);
829
861
  }
830
862
  }
831
863
  };
@@ -860,6 +892,24 @@ var BUILDING_BLOCK_storeSub = (store2, atom2, listener) => {
860
892
  flushCallbacks(store2);
861
893
  };
862
894
  };
895
+ var BUILDING_BLOCK_registerAbortHandler = (store2, promise, abortHandler) => {
896
+ const buildingBlocks = getInternalBuildingBlocks(store2);
897
+ const abortHandlersMap = buildingBlocks[25];
898
+ let abortHandlers = abortHandlersMap.get(promise);
899
+ if (!abortHandlers) {
900
+ abortHandlers = /* @__PURE__ */ new Set();
901
+ abortHandlersMap.set(promise, abortHandlers);
902
+ const cleanup = () => abortHandlersMap.delete(promise);
903
+ promise.then(cleanup, cleanup);
904
+ }
905
+ abortHandlers.add(abortHandler);
906
+ };
907
+ var BUILDING_BLOCK_abortPromise = (store2, promise) => {
908
+ const buildingBlocks = getInternalBuildingBlocks(store2);
909
+ const abortHandlersMap = buildingBlocks[25];
910
+ const abortHandlers = abortHandlersMap.get(promise);
911
+ abortHandlers == null ? void 0 : abortHandlers.forEach((fn) => fn());
912
+ };
863
913
  var buildingBlockMap = /* @__PURE__ */ new WeakMap();
864
914
  var getInternalBuildingBlocks = (store2) => {
865
915
  const buildingBlocks = buildingBlockMap.get(store2);
@@ -870,6 +920,14 @@ var getInternalBuildingBlocks = (store2) => {
870
920
  }
871
921
  return buildingBlocks;
872
922
  };
923
+ function getBuildingBlocks(store2) {
924
+ const buildingBlocks = getInternalBuildingBlocks(store2);
925
+ const enhanceBuildingBlocks = buildingBlocks[24];
926
+ if (enhanceBuildingBlocks) {
927
+ return enhanceBuildingBlocks(buildingBlocks);
928
+ }
929
+ return buildingBlocks;
930
+ }
873
931
  function buildStore(...buildArgs) {
874
932
  const store2 = {
875
933
  get(atom2) {
@@ -920,7 +978,12 @@ function buildStore(...buildArgs) {
920
978
  BUILDING_BLOCK_storeGet,
921
979
  BUILDING_BLOCK_storeSet,
922
980
  BUILDING_BLOCK_storeSub,
923
- void 0
981
+ void 0,
982
+ // abortable promise support
983
+ /* @__PURE__ */ new WeakMap(),
984
+ // abortHandlersMap
985
+ BUILDING_BLOCK_registerAbortHandler,
986
+ BUILDING_BLOCK_abortPromise
924
987
  ].map((fn, i) => buildArgs[i] || fn);
925
988
  buildingBlockMap.set(store2, Object.freeze(buildingBlocks));
926
989
  return store2;
@@ -1020,7 +1083,9 @@ var use = import_react.default.use || // A shim for older React versions
1020
1083
  }
1021
1084
  });
1022
1085
  var continuablePromiseMap = /* @__PURE__ */ new WeakMap();
1023
- var createContinuablePromise = (promise, getValue) => {
1086
+ var createContinuablePromise = (store2, promise, getValue) => {
1087
+ const buildingBlocks = getBuildingBlocks(store2);
1088
+ const registerAbortHandler = buildingBlocks[26];
1024
1089
  let continuablePromise = continuablePromiseMap.get(promise);
1025
1090
  if (!continuablePromise) {
1026
1091
  continuablePromise = new Promise((resolve, reject) => {
@@ -1042,7 +1107,7 @@ var createContinuablePromise = (promise, getValue) => {
1042
1107
  continuablePromiseMap.set(nextValue, continuablePromise);
1043
1108
  curr = nextValue;
1044
1109
  nextValue.then(onFulfilled(nextValue), onRejected(nextValue));
1045
- registerAbortHandler(nextValue, onAbort);
1110
+ registerAbortHandler(store2, nextValue, onAbort);
1046
1111
  } else {
1047
1112
  resolve(nextValue);
1048
1113
  }
@@ -1051,7 +1116,7 @@ var createContinuablePromise = (promise, getValue) => {
1051
1116
  }
1052
1117
  };
1053
1118
  promise.then(onFulfilled(promise), onRejected(promise));
1054
- registerAbortHandler(promise, onAbort);
1119
+ registerAbortHandler(store2, promise, onAbort);
1055
1120
  });
1056
1121
  continuablePromiseMap.set(promise, continuablePromise);
1057
1122
  }
@@ -1083,7 +1148,7 @@ function useAtomValue(atom2, options) {
1083
1148
  const value2 = store2.get(atom2);
1084
1149
  if (isPromiseLike2(value2)) {
1085
1150
  attachPromiseStatus(
1086
- createContinuablePromise(value2, () => store2.get(atom2))
1151
+ createContinuablePromise(store2, value2, () => store2.get(atom2))
1087
1152
  );
1088
1153
  }
1089
1154
  } catch (e) {
@@ -1100,7 +1165,11 @@ function useAtomValue(atom2, options) {
1100
1165
  }, [store2, atom2, delay, promiseStatus]);
1101
1166
  (0, import_react.useDebugValue)(value);
1102
1167
  if (isPromiseLike2(value)) {
1103
- const promise = createContinuablePromise(value, () => store2.get(atom2));
1168
+ const promise = createContinuablePromise(
1169
+ store2,
1170
+ value,
1171
+ () => store2.get(atom2)
1172
+ );
1104
1173
  if (promiseStatus) {
1105
1174
  attachPromiseStatus(promise);
1106
1175
  }
@@ -1134,7 +1203,7 @@ function useStoryContext() {
1134
1203
 
1135
1204
  // src/components/StoryView/ErrorBoundary.tsx
1136
1205
  var import_react2 = __toESM(require("react"));
1137
- var import_react_native = require("react-native");
1206
+ var import_react_native2 = require("react-native");
1138
1207
  var import_jsx_runtime = require("react/jsx-runtime");
1139
1208
  var ErrorBoundary = class extends import_react2.default.Component {
1140
1209
  constructor(props) {
@@ -1150,7 +1219,7 @@ var ErrorBoundary = class extends import_react2.default.Component {
1150
1219
  render() {
1151
1220
  if (this.state.hasError) {
1152
1221
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1153
- import_react_native.View,
1222
+ import_react_native2.View,
1154
1223
  {
1155
1224
  style: {
1156
1225
  margin: 16,
@@ -1161,7 +1230,7 @@ var ErrorBoundary = class extends import_react2.default.Component {
1161
1230
  justifyContent: "center",
1162
1231
  borderRadius: 4
1163
1232
  },
1164
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Text, { style: { fontWeight: "bold" }, children: "Something went wrong rendering your story" })
1233
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native2.Text, { style: { fontWeight: "bold" }, children: "Something went wrong rendering your story" })
1165
1234
  }
1166
1235
  );
1167
1236
  }
@@ -1172,7 +1241,7 @@ var ErrorBoundary = class extends import_react2.default.Component {
1172
1241
  // src/components/StoryView/StoryView.tsx
1173
1242
  var import_jsx_runtime2 = require("react/jsx-runtime");
1174
1243
  function dismissOnStartResponder() {
1175
- import_react_native2.Keyboard.dismiss();
1244
+ import_react_native3.Keyboard.dismiss();
1176
1245
  return false;
1177
1246
  }
1178
1247
  var Text2 = import_react_native_theming.styled.Text(({ theme: theme3 }) => ({
@@ -1213,7 +1282,7 @@ var StoryView = ({
1213
1282
  const { unboundStoryFn: StoryComponent } = context;
1214
1283
  if (useWrapper) {
1215
1284
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1216
- import_react_native2.View,
1285
+ import_react_native3.View,
1217
1286
  {
1218
1287
  style: containerStyle,
1219
1288
  testID: id,
@@ -1227,7 +1296,7 @@ var StoryView = ({
1227
1296
  }
1228
1297
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ErrorBoundary, { onError, children: StoryComponent && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(StoryComponent, { ...context }) });
1229
1298
  }
1230
- return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native2.View, { style: errorContainerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text2, { children: "Please select a story to preview." }) });
1299
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.View, { style: errorContainerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text2, { children: "Please select a story to preview." }) });
1231
1300
  };
1232
1301
  var StoryView_default = import_react3.default.memo(StoryView);
1233
1302
 
@@ -1309,7 +1378,7 @@ var View3 = class {
1309
1378
  if (globalThis.STORYBOOK_WEBSOCKET?.host) {
1310
1379
  return globalThis.STORYBOOK_WEBSOCKET.host;
1311
1380
  }
1312
- return import_react_native3.Platform.OS === "android" ? "10.0.2.2" : "localhost";
1381
+ return import_react_native4.Platform.OS === "android" ? "10.0.2.2" : "localhost";
1313
1382
  };
1314
1383
  __getPort = (params = {}) => {
1315
1384
  if (params.port) {
@@ -1327,14 +1396,15 @@ var View3 = class {
1327
1396
  const websocketType = params.secured ? "wss" : "ws";
1328
1397
  const url = `${websocketType}://${host}${port}/${query}`;
1329
1398
  const channel = new import_channels.Channel({
1399
+ async: true,
1330
1400
  transport: new import_channels.WebsocketTransport({
1331
1401
  url,
1332
1402
  onError: (e) => {
1333
1403
  console.log(`WebsocketTransport error ${JSON.stringify(e)}`);
1334
1404
  }
1335
- }),
1336
- async: true
1405
+ })
1337
1406
  });
1407
+ patchChannelForRN(channel);
1338
1408
  return channel;
1339
1409
  };
1340
1410
  createPreparedStoryMapping = async () => {
@@ -1401,7 +1471,7 @@ var View3 = class {
1401
1471
  return () => {
1402
1472
  const setContext = useSetStoryContext();
1403
1473
  const story = useStoryContext();
1404
- const colorScheme = (0, import_react_native3.useColorScheme)();
1474
+ const colorScheme = (0, import_react_native4.useColorScheme)();
1405
1475
  const [update, forceUpdate] = (0, import_react4.useReducer)((x) => x + 1, 0);
1406
1476
  const [ready, setReady] = (0, import_react4.useState)(false);
1407
1477
  const appliedTheme = (0, import_react4.useMemo)(
@@ -1409,7 +1479,7 @@ var View3 = class {
1409
1479
  [colorScheme]
1410
1480
  );
1411
1481
  (0, import_react4.useEffect)(() => {
1412
- const listener = import_react_native3.Linking.addEventListener("url", ({ url }) => {
1482
+ const listener = import_react_native4.Linking.addEventListener("url", ({ url }) => {
1413
1483
  if (typeof url === "string") {
1414
1484
  const urlObj = new URL(url);
1415
1485
  const storyId = urlObj.searchParams.get(STORYBOOK_STORY_ID_PARAM);
@@ -1434,7 +1504,7 @@ var View3 = class {
1434
1504
  self.createPreparedStoryMapping().then(() => {
1435
1505
  self._ready = true;
1436
1506
  setReady(true);
1437
- return import_react_native3.Linking.getInitialURL().then((url) => {
1507
+ return import_react_native4.Linking.getInitialURL().then((url) => {
1438
1508
  if (url && typeof url === "string") {
1439
1509
  const urlObj = new URL(url);
1440
1510
  const storyId = urlObj.searchParams.get(STORYBOOK_STORY_ID_PARAM);
@@ -1503,14 +1573,14 @@ var View3 = class {
1503
1573
  }, [ready, update]);
1504
1574
  if (!ready) {
1505
1575
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1506
- import_react_native3.View,
1576
+ import_react_native4.View,
1507
1577
  {
1508
1578
  style: {
1509
- ...import_react_native3.StyleSheet.absoluteFillObject,
1579
+ ...import_react_native4.StyleSheet.absoluteFillObject,
1510
1580
  alignItems: "center",
1511
1581
  justifyContent: "center"
1512
1582
  },
1513
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native3.ActivityIndicator, { animating: true, size: "large" })
1583
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native4.ActivityIndicator, { animating: true, size: "large" })
1514
1584
  }
1515
1585
  );
1516
1586
  }
@@ -1555,19 +1625,9 @@ var View3 = class {
1555
1625
  };
1556
1626
  };
1557
1627
 
1558
- // src/Start.tsx
1559
- var import_jsx_runtime7 = require("react/jsx-runtime");
1560
- globalThis.FEATURES = {
1561
- measure: false,
1562
- outline: false,
1563
- interactions: false,
1564
- viewport: false,
1565
- highlight: false,
1566
- backgrounds: false
1567
- };
1568
- if (import_react_native5.Platform.OS === "web" && typeof globalThis.setImmediate === "undefined") {
1569
- require("setimmediate");
1570
- }
1628
+ // src/prepareStories.ts
1629
+ var import_preview_api2 = require("storybook/internal/preview-api");
1630
+ var import_csf2 = require("storybook/internal/csf");
1571
1631
  function prepareStories({
1572
1632
  storyEntries,
1573
1633
  options,
@@ -1666,7 +1726,21 @@ function prepareStories({
1666
1726
  );
1667
1727
  return { index: { v: 5, entries: sorted }, importMap };
1668
1728
  }
1669
- var getProjectAnnotations = (view, annotations) => async () => (0, import_preview_api2.composeConfigs)([
1729
+
1730
+ // src/Start.tsx
1731
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1732
+ globalThis.FEATURES = {
1733
+ measure: false,
1734
+ outline: false,
1735
+ interactions: false,
1736
+ viewport: false,
1737
+ highlight: false,
1738
+ backgrounds: false
1739
+ };
1740
+ if (import_react_native6.Platform.OS === "web" && typeof globalThis.setImmediate === "undefined") {
1741
+ require("setimmediate");
1742
+ }
1743
+ var getProjectAnnotations = (view, annotations) => async () => (0, import_preview_api3.composeConfigs)([
1670
1744
  {
1671
1745
  renderToCanvas: (context) => {
1672
1746
  view._setStory(context.storyContext);
@@ -1688,7 +1762,7 @@ function start({
1688
1762
  storyEntries,
1689
1763
  options
1690
1764
  }) {
1691
- const composedAnnotations = (0, import_preview_api2.composeConfigs)(annotations);
1765
+ const composedAnnotations = (0, import_preview_api3.composeConfigs)(annotations);
1692
1766
  const { index, importMap } = prepareStories({
1693
1767
  storyEntries,
1694
1768
  options,
@@ -1696,7 +1770,7 @@ function start({
1696
1770
  });
1697
1771
  const channel = new import_channels2.Channel({});
1698
1772
  import_manager_api3.addons.setChannel(channel);
1699
- import_preview_api2.addons.setChannel(channel);
1773
+ import_preview_api3.addons.setChannel(channel);
1700
1774
  if (globalThis.FEATURES?.ondeviceBackgrounds) {
1701
1775
  const { registerBackgroundsAddon: registerBackgroundsAddon2 } = (init_register(), __toCommonJS(register_exports));
1702
1776
  registerBackgroundsAddon2();
@@ -1740,7 +1814,7 @@ function start({
1740
1814
  preview.selectionStore.selection = selection;
1741
1815
  }
1742
1816
  };
1743
- const getProjectAnnotationsInitial = async () => (0, import_preview_api2.composeConfigs)([
1817
+ const getProjectAnnotationsInitial = async () => (0, import_preview_api3.composeConfigs)([
1744
1818
  {
1745
1819
  renderToCanvas: (context) => {
1746
1820
  view._setStory(context.storyContext);
@@ -1757,7 +1831,7 @@ function start({
1757
1831
  },
1758
1832
  ...annotations
1759
1833
  ]);
1760
- const preview = new import_preview_api2.PreviewWithSelection(
1834
+ const preview = new import_preview_api3.PreviewWithSelection(
1761
1835
  async (importPath) => importMap[importPath],
1762
1836
  getProjectAnnotationsInitial,
1763
1837
  selectionStore,
@@ -1773,7 +1847,7 @@ function start({
1773
1847
  return view;
1774
1848
  }
1775
1849
  function updateView(viewInstance, annotations, normalizedStories, options) {
1776
- const composedAnnotations = (0, import_preview_api2.composeConfigs)(annotations);
1850
+ const composedAnnotations = (0, import_preview_api3.composeConfigs)(annotations);
1777
1851
  const storySort = composedAnnotations.parameters?.options?.storySort;
1778
1852
  const { importMap, index } = prepareStories({
1779
1853
  storyEntries: normalizedStories,
@@ -856,7 +856,12 @@ function createChannelServer({
856
856
  ws.on("message", function message(data) {
857
857
  try {
858
858
  const json = JSON.parse(data.toString());
859
- wss.clients.forEach((wsClient) => wsClient.send(JSON.stringify(json)));
859
+ const msg = JSON.stringify(json);
860
+ wss.clients.forEach((wsClient) => {
861
+ if (wsClient !== ws && wsClient.readyState === import_ws.WebSocket.OPEN) {
862
+ wsClient.send(msg);
863
+ }
864
+ });
860
865
  } catch (error) {
861
866
  console.error(error);
862
867
  }
package/dist/node.js CHANGED
@@ -635,7 +635,12 @@ function createChannelServer({
635
635
  ws.on("message", function message(data) {
636
636
  try {
637
637
  const json = JSON.parse(data.toString());
638
- wss.clients.forEach((wsClient) => wsClient.send(JSON.stringify(json)));
638
+ const msg = JSON.stringify(json);
639
+ wss.clients.forEach((wsClient) => {
640
+ if (wsClient !== ws && wsClient.readyState === import_ws.WebSocket.OPEN) {
641
+ wsClient.send(msg);
642
+ }
643
+ });
639
644
  } catch (error) {
640
645
  console.error(error);
641
646
  }
package/dist/preview.js CHANGED
@@ -22,7 +22,12 @@ __export(preview_exports, {
22
22
  default: () => preview_default
23
23
  });
24
24
  module.exports = __toCommonJS(preview_exports);
25
+ var import_react_native = require("react-native");
25
26
  var import_entry_preview_docs = require("@storybook/react/entry-preview-docs");
27
+ if (import_react_native.Platform.OS === "web") {
28
+ globalThis.ProgressTransitionRegister = {};
29
+ globalThis.UpdatePropsManager = {};
30
+ }
26
31
  var preview = {
27
32
  argTypesEnhancers: import_entry_preview_docs.argTypesEnhancers,
28
33
  parameters: {
@@ -854,7 +854,12 @@ function createChannelServer({
854
854
  ws.on("message", function message(data) {
855
855
  try {
856
856
  const json = JSON.parse(data.toString());
857
- wss.clients.forEach((wsClient) => wsClient.send(JSON.stringify(json)));
857
+ const msg = JSON.stringify(json);
858
+ wss.clients.forEach((wsClient) => {
859
+ if (wsClient !== ws && wsClient.readyState === import_ws.WebSocket.OPEN) {
860
+ wsClient.send(msg);
861
+ }
862
+ });
858
863
  } catch (error) {
859
864
  console.error(error);
860
865
  }
@@ -0,0 +1,12 @@
1
+ const { transform } = require('sucrase');
2
+
3
+ module.exports = {
4
+ process(src, filename) {
5
+ const result = transform(src, {
6
+ transforms: ['imports'],
7
+ filePath: filename,
8
+ sourceMapOptions: { compiledFilename: filename },
9
+ });
10
+ return { code: result.code, map: result.sourceMap };
11
+ },
12
+ };
package/jest.config.js CHANGED
@@ -2,9 +2,15 @@
2
2
  const config = {
3
3
  preset: 'jest-expo',
4
4
  setupFiles: ['<rootDir>/setup.js'],
5
+ ...(process.env.JEST_CACHE_DIRECTORY && { cacheDirectory: process.env.JEST_CACHE_DIRECTORY }),
6
+
7
+ transform: {
8
+ 'node_modules/(storybook|@storybook/react)/.+\\.[jt]sx?$':
9
+ '<rootDir>/jest-sucrase-transformer.js',
10
+ },
5
11
 
6
12
  transformIgnorePatterns: [
7
- 'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg|storybook/.*|@storybook/.*|uuid)',
13
+ 'node_modules/(?!((jest-)?react-native|@react-native(-community)?)|jest-expo|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@sentry/react-native|native-base|react-native-svg|storybook|@storybook/react|uuid|@react-native/.*)',
8
14
  ],
9
15
  testPathIgnorePatterns: ['/node_modules/', '/scripts/generate\\.test\\.js$'],
10
16
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@storybook/react-native",
3
- "version": "10.3.0-next.1",
3
+ "version": "10.3.0-next.3",
4
4
  "description": "A better way to develop React Native Components for your app",
5
5
  "keywords": [
6
6
  "react",
@@ -43,10 +43,13 @@
43
43
  "metro/**/*"
44
44
  ],
45
45
  "dependencies": {
46
- "@storybook/react": "10.3.0-alpha.9",
47
- "@storybook/react-native-theming": "^10.3.0-next.1",
48
- "@storybook/react-native-ui": "^10.3.0-next.1",
49
- "@storybook/react-native-ui-common": "^10.3.0-next.1",
46
+ "@storybook/mcp": "^0.4.1",
47
+ "@storybook/react": "10.3.0-alpha.12",
48
+ "@storybook/react-native-theming": "^10.3.0-next.3",
49
+ "@storybook/react-native-ui": "^10.3.0-next.3",
50
+ "@storybook/react-native-ui-common": "^10.3.0-next.3",
51
+ "@tmcp/adapter-valibot": "^0.1.4",
52
+ "@tmcp/transport-http": "^0.8.0",
50
53
  "commander": "^14.0.2",
51
54
  "dedent": "^1.7.0",
52
55
  "deepmerge": "^4.3.1",
@@ -54,24 +57,21 @@
54
57
  "glob": "^13.0.0",
55
58
  "react-native-url-polyfill": "^3.0.0",
56
59
  "setimmediate": "^1.0.5",
57
- "ws": "^8.18.3",
58
- "@storybook/mcp": "^0.4.1",
59
60
  "tmcp": "^1.16.0",
60
- "@tmcp/adapter-valibot": "^0.1.4",
61
- "@tmcp/transport-http": "^0.8.0",
62
- "valibot": "^1.2.0"
61
+ "valibot": "^1.2.0",
62
+ "ws": "^8.18.3"
63
63
  },
64
64
  "devDependencies": {
65
65
  "@types/jest": "^29.4.3",
66
- "@types/react": "~19.1.10",
66
+ "@types/react": "~19.2.14",
67
67
  "babel-jest": "^29.7.0",
68
68
  "babel-preset-expo": "^54.0.6",
69
69
  "jest": "^29.7.0",
70
- "jest-expo": "~54.0.17",
70
+ "jest-expo": "~55.0.9",
71
71
  "jotai": "^2.17.1",
72
- "react": "19.1.0",
73
- "react-native": "0.81.5",
74
- "storybook": "10.3.0-alpha.9",
72
+ "react": "19.2.0",
73
+ "react-native": "0.83.2",
74
+ "storybook": "10.3.0-alpha.12",
75
75
  "test-renderer": "^0.14.0",
76
76
  "tsup": "^8.5.0",
77
77
  "typescript": "~5.9.3"
package/readme.md CHANGED
@@ -448,12 +448,25 @@ Type: `boolean`, default: `false`
448
448
 
449
449
  Whether to use lite mode for Storybook. In lite mode, the default Storybook UI is mocked out so you don't need to install all its dependencies like react-native-reanimated. This is useful for reducing bundle size and dependencies. Use this when using @storybook/react-native-ui-lite instead of @storybook/react-native-ui.
450
450
 
451
+ #### experimental_mcp
452
+
453
+ Type: `boolean`, default: `false`
454
+
455
+ Enables an experimental MCP (Model Context Protocol) endpoint at `/mcp` on the Storybook channel server. This can be used by AI tooling to query Storybook documentation and component/story metadata. Available from v10.3 onwards.
456
+
457
+ You can enable MCP with or without websockets:
458
+
459
+ - `experimental_mcp: true` starts the HTTP MCP endpoint
460
+ - adding `websockets` also enables story selection tools over the same channel server
461
+
451
462
  ### websockets
452
463
 
453
- Type: `{ host: string?, port: number? }`, default: `undefined`
464
+ Type: `'auto' | { host: string?, port: number? }`, default: `undefined`
454
465
 
455
466
  If specified, create a WebSocket server on startup. This allows you to sync up multiple devices to show the same story and [arg](https://storybook.js.org/docs/writing-stories/args) values connected to the story in the UI.
456
467
 
468
+ Use `'auto'` to automatically detect your LAN IP and inject host/port into the generated `storybook.requires` file.
469
+
457
470
  ### websockets.host
458
471
 
459
472
  Type: `string`, default: `'localhost'`