Skip to content
This repository was archived by the owner on Aug 22, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 9 additions & 35 deletions App.tsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,21 @@
import React, { useState } from 'react';
import { addItem } from 'react-native-dev-menu';
import React from 'react';

import {
ApolloProvider,
AuthProvider,
NavigationProvider,
StorybookProvider,
} from '@app/providers';

import Storybook from './.storybook/Storybook';

/**
* Add item to DevMenu (Cmd+D) to toggle between the app and Storybook.
*/
const addDevMenuItemForStorybook = (
setShowStorybook: React.Dispatch<React.SetStateAction<boolean>>,
showStorybook: boolean,
) => {
addItem('Toggle Storybook', () => setShowStorybook(!showStorybook)).catch(
error =>
error instanceof Error &&
console.error(
`DevMenu.addItem "Toggle Storybook" error: ${error.toString()}`,
),
);
};

export const App = () => {
const [showStorybook, setShowStorybook] = useState(false);

if (__DEV__) {
addDevMenuItemForStorybook(setShowStorybook, showStorybook);

if (showStorybook) {
return <Storybook />;
}
}

return (
<AuthProvider>
<ApolloProvider>
<NavigationProvider />
</ApolloProvider>
</AuthProvider>
<StorybookProvider>
<AuthProvider>
<ApolloProvider>
<NavigationProvider />
</ApolloProvider>
</AuthProvider>
</StorybookProvider>
);
};

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ Commands are defined in the [`Justfile`](Justfile) and can be listed with [`just

## Storybook

Storybook can be toggled via the [DevMenu](https://github.com/zoontek/react-native-dev-menu) (CMD+D).
Storybook can be toggled via the ReactNative developer menu (open them by shaking the device e.g. with CMD+CTRL+Z).
5 changes: 5 additions & 0 deletions __mocks__/react-native/Libraries/Utilities/DevSettings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const DevSettings = {
addMenuItem: jest.fn(),
};

export default DevSettings;
4 changes: 2 additions & 2 deletions __tests__/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { render } from '@testing-library/react-native';

import { App } from '../App';

jest.mock('react-native-dev-menu', () => ({
addItem: jest.fn(() => Promise.resolve()),
jest.mock('react-native/Libraries/Utilities/DevSettings', () => ({
addMenuItem: jest.fn(),
}));

jest.mock('@app/hooks/useAuth', () => ({
Expand Down
8 changes: 0 additions & 8 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -506,10 +506,6 @@ PODS:
- React-Core
- RNDateTimePicker (7.6.0):
- React-Core
- RNDevMenu (4.1.1):
- React-Core
- React-Core/DevSupport
- React-RCTNetwork
- RNGestureHandler (2.13.1):
- React-Core
- RNReanimated (3.5.4):
Expand Down Expand Up @@ -619,7 +615,6 @@ DEPENDENCIES:
- ReactNativeUiLib (from `../node_modules/react-native-ui-lib`)
- "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)"
- "RNDateTimePicker (from `../node_modules/@react-native-community/datetimepicker`)"
- RNDevMenu (from `../node_modules/react-native-dev-menu`)
- RNGestureHandler (from `../node_modules/react-native-gesture-handler`)
- RNReanimated (from `../node_modules/react-native-reanimated`)
- RNScreens (from `../node_modules/react-native-screens`)
Expand Down Expand Up @@ -735,8 +730,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/@react-native-async-storage/async-storage"
RNDateTimePicker:
:path: "../node_modules/@react-native-community/datetimepicker"
RNDevMenu:
:path: "../node_modules/react-native-dev-menu"
RNGestureHandler:
:path: "../node_modules/react-native-gesture-handler"
RNReanimated:
Expand Down Expand Up @@ -805,7 +798,6 @@ SPEC CHECKSUMS:
ReactNativeUiLib: 511a5eb03809a0b27f6aefa2b4d6f7c2d6ae4053
RNCAsyncStorage: c913ede1fa163a71cea118ed4670bbaaa4b511bb
RNDateTimePicker: ccd988deb223cbb2e669e157ec576c2c6217128c
RNDevMenu: 72807568fe4188bd4c40ce32675d82434b43c45d
RNGestureHandler: 38aa38413896620338948fbb5c90579a7b1c3fde
RNReanimated: ab2e96c6d5591c3dfbb38a464f54c8d17fb34a87
RNScreens: 85d3880b52d34db7b8eeebe2f1a0e807c05e69fa
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
"react-dom": "18.2.0",
"react-native": "0.72.5",
"react-native-app-auth": "^7.1.0",
"react-native-dev-menu": "^4.1.1",
"react-native-dotenv": "^3.4.9",
"react-native-encrypted-storage": "^4.0.3",
"react-native-gesture-handler": "^2.13.1",
Expand Down
55 changes: 55 additions & 0 deletions src/providers/StorybookProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* istanbul ignore file */

import React, { useState, useEffect, useCallback } from 'react';
// We need to import directly from the file instead of from react-native
// because we need to mock this in the tests.
// @ts-expect-error It's falsely reported that DevSettings has no exported member addMenuItem,
// but there actually is: https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Utilities/DevSettings.d.ts
import { addMenuItem } from 'react-native/Libraries/Utilities/DevSettings';

export type StorybookProviderType = React.FC<{ children: React.ReactNode }>;

/**
* This provider adds a "Toggle Storybook" item to the ReactNative developer menu but only
* if the app is running in development mode.
*
* The ReactNative developer menu can be opened by shaking the device e.g. on Mac in the iOS
* simulator press Cmd+Ctrl+Z.
*
* NOTE: If __DEV__ is false, nothing related to Storybook is rendered and this provider does
* basically nothing.
*/
export const StorybookProvider: StorybookProviderType = ({
children,
}: {
children: React.ReactNode;
}) => {
const [showStorybook, setShowStorybook] = useState(false);
const toggleStorybook = useCallback(
() => setShowStorybook(previousState => !previousState),
[],
);

useEffect(
() => {
if (__DEV__) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
addMenuItem('Toggle Storybook', toggleStorybook);
}
},
// We only want to run this hook once.
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
);

if (__DEV__) {
// eslint-disable-next-line @typescript-eslint/no-var-requires,@typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
const Storybook = require('../../.storybook/Storybook').default;

if (showStorybook) {
return <Storybook />;
}
}

return children;
};
1 change: 1 addition & 0 deletions src/providers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './ApolloProvider';
export * from './AuthProvider';
export * from './NavigationProvider';
export * from './StorybookProvider';
10 changes: 0 additions & 10 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7308,7 +7308,6 @@ __metadata:
react-dom: 18.2.0
react-native: 0.72.5
react-native-app-auth: ^7.1.0
react-native-dev-menu: ^4.1.1
react-native-dotenv: ^3.4.9
react-native-encrypted-storage: ^4.0.3
react-native-gesture-handler: ^2.13.1
Expand Down Expand Up @@ -17380,15 +17379,6 @@ __metadata:
languageName: node
linkType: hard

"react-native-dev-menu@npm:^4.1.1":
version: 4.1.1
resolution: "react-native-dev-menu@npm:4.1.1"
peerDependencies:
react-native: ">=0.61.0"
checksum: 13b7b37cab629d71e49f684a343e858243c7b7e79fd85c8a08ae4886c4cf24ca086610baa4c3c4b4f4ecaf8834f1680ecabdac4921f207c47c1b4f1a9f7d44dd
languageName: node
linkType: hard

"react-native-dotenv@npm:^3.4.9":
version: 3.4.9
resolution: "react-native-dotenv@npm:3.4.9"
Expand Down