@bbearai/react-native 0.1.0
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/README.md +196 -0
- package/dist/index.d.mts +60 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +1664 -0
- package/dist/index.mjs +1640 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# @bugbearai/react-native
|
|
2
|
+
|
|
3
|
+
BugBear QA widget for React Native mobile apps.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @bugbearai/react-native @bugbearai/core
|
|
9
|
+
# or
|
|
10
|
+
yarn add @bugbearai/react-native @bugbearai/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { BugBearProvider, BugBearWidget } from '@bugbearai/react-native';
|
|
17
|
+
|
|
18
|
+
function App() {
|
|
19
|
+
return (
|
|
20
|
+
<BugBearProvider
|
|
21
|
+
projectId="your-project-id"
|
|
22
|
+
getCurrentUser={async () => ({
|
|
23
|
+
id: user.id,
|
|
24
|
+
email: user.email,
|
|
25
|
+
})}
|
|
26
|
+
>
|
|
27
|
+
<YourApp />
|
|
28
|
+
<BugBearWidget />
|
|
29
|
+
</BugBearProvider>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Monorepo Setup
|
|
35
|
+
|
|
36
|
+
When using BugBear in a monorepo (e.g., with Turborepo, Nx, or Yarn Workspaces), you may encounter React duplicate instance errors like:
|
|
37
|
+
|
|
38
|
+
- "Invalid hook call"
|
|
39
|
+
- "Cannot read property 'useState' of null"
|
|
40
|
+
- "Hooks can only be called inside the body of a function component"
|
|
41
|
+
|
|
42
|
+
This happens when Metro (React Native's bundler) picks up different React instances from different `node_modules` folders.
|
|
43
|
+
|
|
44
|
+
### Fix: Configure Metro to use a single React instance
|
|
45
|
+
|
|
46
|
+
Update your `metro.config.js` in the React Native app:
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
const path = require('path');
|
|
50
|
+
const { getDefaultConfig } = require('@react-native/metro-config');
|
|
51
|
+
|
|
52
|
+
// Get the monorepo root (adjust path as needed)
|
|
53
|
+
const monorepoRoot = path.resolve(__dirname, '../..');
|
|
54
|
+
|
|
55
|
+
const config = getDefaultConfig(__dirname);
|
|
56
|
+
|
|
57
|
+
// Block React from monorepo root to prevent duplicates
|
|
58
|
+
config.resolver.blockList = [
|
|
59
|
+
...(config.resolver.blockList || []),
|
|
60
|
+
new RegExp(`${monorepoRoot}/node_modules/react/.*`),
|
|
61
|
+
new RegExp(`${monorepoRoot}/node_modules/react-native/.*`),
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
// Ensure Metro uses the app's React instance
|
|
65
|
+
config.resolver.extraNodeModules = {
|
|
66
|
+
react: path.resolve(__dirname, 'node_modules/react'),
|
|
67
|
+
'react-native': path.resolve(__dirname, 'node_modules/react-native'),
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// Watch the monorepo packages
|
|
71
|
+
config.watchFolders = [
|
|
72
|
+
monorepoRoot,
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
module.exports = config;
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Alternative: Yarn/npm Workspaces hoisting
|
|
79
|
+
|
|
80
|
+
If you're using Yarn workspaces, you can also configure hoisting to ensure a single React version:
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
// package.json (workspace root)
|
|
84
|
+
{
|
|
85
|
+
"workspaces": {
|
|
86
|
+
"packages": ["apps/*", "packages/*"],
|
|
87
|
+
"nohoist": [
|
|
88
|
+
"**/react-native",
|
|
89
|
+
"**/react-native/**"
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Configuration Options
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
<BugBearProvider
|
|
99
|
+
projectId="your-project-id" // Required: Your BugBear project ID
|
|
100
|
+
getCurrentUser={async () => {...}} // Required: Return current user info
|
|
101
|
+
getNavigationHistory={() => [...]} // Optional: Custom navigation tracking
|
|
102
|
+
onReportSubmitted={(report) => {...}} // Optional: Callback after report submission
|
|
103
|
+
>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Navigation Tracking
|
|
107
|
+
|
|
108
|
+
BugBear can automatically track navigation history for better bug context. If you're using React Navigation:
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
import { useNavigationContainerRef } from '@react-navigation/native';
|
|
112
|
+
|
|
113
|
+
function App() {
|
|
114
|
+
const navigationRef = useNavigationContainerRef();
|
|
115
|
+
const routeNameRef = useRef<string>();
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<BugBearProvider
|
|
119
|
+
projectId="your-project-id"
|
|
120
|
+
getCurrentUser={...}
|
|
121
|
+
getNavigationHistory={() => routeNameRef.current ? [routeNameRef.current] : []}
|
|
122
|
+
>
|
|
123
|
+
<NavigationContainer
|
|
124
|
+
ref={navigationRef}
|
|
125
|
+
onReady={() => {
|
|
126
|
+
routeNameRef.current = navigationRef.getCurrentRoute()?.name;
|
|
127
|
+
}}
|
|
128
|
+
onStateChange={() => {
|
|
129
|
+
const currentRouteName = navigationRef.getCurrentRoute()?.name;
|
|
130
|
+
routeNameRef.current = currentRouteName;
|
|
131
|
+
}}
|
|
132
|
+
>
|
|
133
|
+
{/* Your navigation */}
|
|
134
|
+
</NavigationContainer>
|
|
135
|
+
<BugBearWidget />
|
|
136
|
+
</BugBearProvider>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Button Configuration
|
|
142
|
+
|
|
143
|
+
The BugBear button is draggable by default with edge-snapping behavior:
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
<BugBearButton
|
|
147
|
+
draggable={true} // Enable/disable dragging (default: true)
|
|
148
|
+
position="bottom-right" // Initial position: 'bottom-right' | 'bottom-left'
|
|
149
|
+
initialX={100} // Custom initial X position (optional)
|
|
150
|
+
initialY={500} // Custom initial Y position (optional)
|
|
151
|
+
minY={100} // Minimum Y from top (default: 100)
|
|
152
|
+
maxYOffset={160} // Max Y offset from bottom (default: 160)
|
|
153
|
+
/>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The button automatically snaps to the nearest screen edge when released.
|
|
157
|
+
|
|
158
|
+
## Troubleshooting
|
|
159
|
+
|
|
160
|
+
### Widget not showing
|
|
161
|
+
|
|
162
|
+
1. Make sure the current user's email is registered as a tester in the BugBear dashboard
|
|
163
|
+
2. Verify QA mode is enabled for your project
|
|
164
|
+
3. Check that `getCurrentUser` returns a valid email
|
|
165
|
+
|
|
166
|
+
### Supabase connection issues
|
|
167
|
+
|
|
168
|
+
If you're using your own Supabase instance, provide the credentials:
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
<BugBearProvider
|
|
172
|
+
projectId="your-project-id"
|
|
173
|
+
supabaseUrl="https://your-instance.supabase.co"
|
|
174
|
+
supabaseAnonKey="your-anon-key"
|
|
175
|
+
getCurrentUser={...}
|
|
176
|
+
>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Known Limitations
|
|
180
|
+
|
|
181
|
+
### Button hidden behind modals
|
|
182
|
+
|
|
183
|
+
The BugBear button renders within the normal React Native view hierarchy using absolute positioning. When your app displays modals, bottom sheets, or the keyboard, the button may be hidden behind them.
|
|
184
|
+
|
|
185
|
+
This is a platform limitation - React Native doesn't provide a built-in way to render views above all modals without using a Modal component (which would block touch events on the underlying app).
|
|
186
|
+
|
|
187
|
+
**Workarounds:**
|
|
188
|
+
- Accept that the button won't be accessible when modals are open
|
|
189
|
+
- Consider dismissing the modal to access BugBear
|
|
190
|
+
- For testing flows that involve modals, provide an alternative way to trigger the BugBear widget
|
|
191
|
+
|
|
192
|
+
We're investigating solutions using `react-native-portal` or similar libraries for future releases.
|
|
193
|
+
|
|
194
|
+
## License
|
|
195
|
+
|
|
196
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { BugBearConfig, BugBearClient, TesterInfo, TestAssignment, DeviceInfo, TesterThread, TesterMessage, AppContext } from '@bbearai/core';
|
|
3
|
+
export { AppContext, BugBearConfig, BugBearReport, DeviceInfo, MessageSenderType, ReportType, Severity, TestAssignment, TesterInfo, TesterMessage, TesterThread, ThreadPriority, ThreadType } from '@bbearai/core';
|
|
4
|
+
|
|
5
|
+
interface BugBearContextValue {
|
|
6
|
+
client: BugBearClient | null;
|
|
7
|
+
isTester: boolean;
|
|
8
|
+
isQAEnabled: boolean;
|
|
9
|
+
shouldShowWidget: boolean;
|
|
10
|
+
testerInfo: TesterInfo | null;
|
|
11
|
+
assignments: TestAssignment[];
|
|
12
|
+
currentAssignment: TestAssignment | null;
|
|
13
|
+
refreshAssignments: () => Promise<void>;
|
|
14
|
+
isLoading: boolean;
|
|
15
|
+
getDeviceInfo: () => DeviceInfo;
|
|
16
|
+
/** Navigate to a route/screen (for deep linking from test cases) */
|
|
17
|
+
onNavigate?: (route: string) => void;
|
|
18
|
+
/** Message threads for the current tester */
|
|
19
|
+
threads: TesterThread[];
|
|
20
|
+
/** Total unread message count */
|
|
21
|
+
unreadCount: number;
|
|
22
|
+
/** Refresh message threads */
|
|
23
|
+
refreshThreads: () => Promise<void>;
|
|
24
|
+
/** Get all messages in a thread */
|
|
25
|
+
getThreadMessages: (threadId: string) => Promise<TesterMessage[]>;
|
|
26
|
+
/** Send a message to a thread */
|
|
27
|
+
sendMessage: (threadId: string, content: string) => Promise<boolean>;
|
|
28
|
+
/** Mark a thread as read */
|
|
29
|
+
markAsRead: (threadId: string) => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
declare function useBugBear(): BugBearContextValue;
|
|
32
|
+
interface BugBearProviderProps {
|
|
33
|
+
config: BugBearConfig;
|
|
34
|
+
children: ReactNode;
|
|
35
|
+
/** App version string */
|
|
36
|
+
appVersion?: string;
|
|
37
|
+
}
|
|
38
|
+
declare function BugBearProvider({ config, children, appVersion }: BugBearProviderProps): React.JSX.Element;
|
|
39
|
+
|
|
40
|
+
interface BugBearButtonProps {
|
|
41
|
+
/** Get current app context */
|
|
42
|
+
getAppContext?: () => AppContext;
|
|
43
|
+
/** Position of the floating button (used as initial position when draggable) */
|
|
44
|
+
position?: 'bottom-right' | 'bottom-left';
|
|
45
|
+
/** Custom button style */
|
|
46
|
+
buttonStyle?: object;
|
|
47
|
+
/** Enable draggable button with edge snapping (default: true) */
|
|
48
|
+
draggable?: boolean;
|
|
49
|
+
/** Initial X position (overrides position prop) */
|
|
50
|
+
initialX?: number;
|
|
51
|
+
/** Initial Y position (overrides position prop) */
|
|
52
|
+
initialY?: number;
|
|
53
|
+
/** Minimum Y position from top of screen */
|
|
54
|
+
minY?: number;
|
|
55
|
+
/** Maximum Y position from bottom of screen */
|
|
56
|
+
maxYOffset?: number;
|
|
57
|
+
}
|
|
58
|
+
declare function BugBearButton({ getAppContext, position, buttonStyle, draggable, initialX, initialY, minY, maxYOffset, }: BugBearButtonProps): React.JSX.Element | null;
|
|
59
|
+
|
|
60
|
+
export { BugBearButton, BugBearProvider, useBugBear };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React, { ReactNode } from 'react';
|
|
2
|
+
import { BugBearConfig, BugBearClient, TesterInfo, TestAssignment, DeviceInfo, TesterThread, TesterMessage, AppContext } from '@bbearai/core';
|
|
3
|
+
export { AppContext, BugBearConfig, BugBearReport, DeviceInfo, MessageSenderType, ReportType, Severity, TestAssignment, TesterInfo, TesterMessage, TesterThread, ThreadPriority, ThreadType } from '@bbearai/core';
|
|
4
|
+
|
|
5
|
+
interface BugBearContextValue {
|
|
6
|
+
client: BugBearClient | null;
|
|
7
|
+
isTester: boolean;
|
|
8
|
+
isQAEnabled: boolean;
|
|
9
|
+
shouldShowWidget: boolean;
|
|
10
|
+
testerInfo: TesterInfo | null;
|
|
11
|
+
assignments: TestAssignment[];
|
|
12
|
+
currentAssignment: TestAssignment | null;
|
|
13
|
+
refreshAssignments: () => Promise<void>;
|
|
14
|
+
isLoading: boolean;
|
|
15
|
+
getDeviceInfo: () => DeviceInfo;
|
|
16
|
+
/** Navigate to a route/screen (for deep linking from test cases) */
|
|
17
|
+
onNavigate?: (route: string) => void;
|
|
18
|
+
/** Message threads for the current tester */
|
|
19
|
+
threads: TesterThread[];
|
|
20
|
+
/** Total unread message count */
|
|
21
|
+
unreadCount: number;
|
|
22
|
+
/** Refresh message threads */
|
|
23
|
+
refreshThreads: () => Promise<void>;
|
|
24
|
+
/** Get all messages in a thread */
|
|
25
|
+
getThreadMessages: (threadId: string) => Promise<TesterMessage[]>;
|
|
26
|
+
/** Send a message to a thread */
|
|
27
|
+
sendMessage: (threadId: string, content: string) => Promise<boolean>;
|
|
28
|
+
/** Mark a thread as read */
|
|
29
|
+
markAsRead: (threadId: string) => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
declare function useBugBear(): BugBearContextValue;
|
|
32
|
+
interface BugBearProviderProps {
|
|
33
|
+
config: BugBearConfig;
|
|
34
|
+
children: ReactNode;
|
|
35
|
+
/** App version string */
|
|
36
|
+
appVersion?: string;
|
|
37
|
+
}
|
|
38
|
+
declare function BugBearProvider({ config, children, appVersion }: BugBearProviderProps): React.JSX.Element;
|
|
39
|
+
|
|
40
|
+
interface BugBearButtonProps {
|
|
41
|
+
/** Get current app context */
|
|
42
|
+
getAppContext?: () => AppContext;
|
|
43
|
+
/** Position of the floating button (used as initial position when draggable) */
|
|
44
|
+
position?: 'bottom-right' | 'bottom-left';
|
|
45
|
+
/** Custom button style */
|
|
46
|
+
buttonStyle?: object;
|
|
47
|
+
/** Enable draggable button with edge snapping (default: true) */
|
|
48
|
+
draggable?: boolean;
|
|
49
|
+
/** Initial X position (overrides position prop) */
|
|
50
|
+
initialX?: number;
|
|
51
|
+
/** Initial Y position (overrides position prop) */
|
|
52
|
+
initialY?: number;
|
|
53
|
+
/** Minimum Y position from top of screen */
|
|
54
|
+
minY?: number;
|
|
55
|
+
/** Maximum Y position from bottom of screen */
|
|
56
|
+
maxYOffset?: number;
|
|
57
|
+
}
|
|
58
|
+
declare function BugBearButton({ getAppContext, position, buttonStyle, draggable, initialX, initialY, minY, maxYOffset, }: BugBearButtonProps): React.JSX.Element | null;
|
|
59
|
+
|
|
60
|
+
export { BugBearButton, BugBearProvider, useBugBear };
|