Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve dev menu loading #118

Merged
merged 4 commits into from Oct 10, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 9 additions & 35 deletions App.tsx
@@ -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
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
@@ -0,0 +1,5 @@
const DevSettings = {
addMenuItem: jest.fn(),
};

export default DevSettings;
4 changes: 2 additions & 2 deletions __tests__/App.test.tsx
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
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
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
@@ -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
@@ -1,3 +1,4 @@
export * from './ApolloProvider';
export * from './AuthProvider';
export * from './NavigationProvider';
export * from './StorybookProvider';
10 changes: 0 additions & 10 deletions yarn.lock
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