mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
203f3e3399 | ||
|
|
e7c289b56e | ||
|
|
3cfbae6540 | ||
|
|
3fd98d95ef | ||
|
|
c194ed2dbb | ||
|
|
93bbab3878 | ||
|
|
1a305fd79b | ||
|
|
45a43241cd | ||
|
|
041c392ec9 |
@@ -83,7 +83,7 @@ function reportException(e: ExtendedError, isFatal: boolean) {
|
||||
e.jsEngine == null ? message : `${message}, js engine: ${e.jsEngine}`;
|
||||
|
||||
const isHandledByLogBox =
|
||||
e.forceRedbox !== true && global.__reactExperimentalLogBox;
|
||||
e.forceRedbox !== true && global.__unstable_isLogBoxEnabled === true;
|
||||
|
||||
const data = preprocessException({
|
||||
message,
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/**
|
||||
* @generated by scripts/bump-oss-version.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @generated by scripts/bump-oss-version.js
|
||||
* @flow
|
||||
*/
|
||||
|
||||
exports.version = {
|
||||
major: 0,
|
||||
minor: 0,
|
||||
minor: 62,
|
||||
patch: 0,
|
||||
prerelease: null,
|
||||
prerelease: 'rc.0',
|
||||
};
|
||||
|
||||
@@ -1671,6 +1671,33 @@ namespace facebook {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
|
||||
static facebook::jsi::Value __hostFunction_NativeLogBoxSpecJSI_show(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
||||
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "show", @selector(show), args, count);
|
||||
}
|
||||
|
||||
static facebook::jsi::Value __hostFunction_NativeLogBoxSpecJSI_hide(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
||||
return static_cast<ObjCTurboModule&>(turboModule).invokeObjCMethod(rt, VoidKind, "hide", @selector(hide), args, count);
|
||||
}
|
||||
|
||||
|
||||
NativeLogBoxSpecJSI::NativeLogBoxSpecJSI(id<RCTTurboModule> instance, std::shared_ptr<CallInvoker> jsInvoker)
|
||||
: ObjCTurboModule("LogBox", instance, jsInvoker) {
|
||||
|
||||
methodMap_["show"] = MethodMetadata {0, __hostFunction_NativeLogBoxSpecJSI_show};
|
||||
|
||||
|
||||
methodMap_["hide"] = MethodMetadata {0, __hostFunction_NativeLogBoxSpecJSI_hide};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
} // namespace react
|
||||
|
||||
@@ -1771,6 +1771,25 @@ namespace facebook {
|
||||
};
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
@protocol NativeLogBoxSpec <RCTBridgeModule, RCTTurboModule>
|
||||
|
||||
- (void)show;
|
||||
- (void)hide;
|
||||
|
||||
@end
|
||||
namespace facebook {
|
||||
namespace react {
|
||||
/**
|
||||
* ObjC++ class for module 'LogBox'
|
||||
*/
|
||||
|
||||
class JSI_EXPORT NativeLogBoxSpecJSI : public ObjCTurboModule {
|
||||
public:
|
||||
NativeLogBoxSpecJSI(id<RCTTurboModule> instance, std::shared_ptr<CallInvoker> jsInvoker);
|
||||
|
||||
};
|
||||
} // namespace react
|
||||
} // namespace facebook
|
||||
@protocol NativeModalManagerSpec <RCTBridgeModule, RCTTurboModule>
|
||||
|
||||
- (void)addListener:(NSString *)eventName;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
('use strict');
|
||||
|
||||
import * as React from 'react';
|
||||
import LogBoxLog from './LogBoxLog';
|
||||
import {parseLogBoxException} from './parseLogBoxLog';
|
||||
import type {LogLevel} from './LogBoxLog';
|
||||
@@ -190,6 +191,9 @@ function appendNewLog(newLog) {
|
||||
if (addPendingLog && status !== 'PENDING') {
|
||||
addPendingLog();
|
||||
clearTimeout(optimisticTimeout);
|
||||
} else if (status !== 'PENDING') {
|
||||
// The log has already been added but we need to trigger a render.
|
||||
handleUpdate();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@@ -369,3 +373,97 @@ export function observe(observer: Observer): Subscription {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
type Props = $ReadOnly<{||}>;
|
||||
type State = $ReadOnly<{|
|
||||
logs: LogBoxLogs,
|
||||
isDisabled: boolean,
|
||||
hasError: boolean,
|
||||
selectedLogIndex: number,
|
||||
|}>;
|
||||
|
||||
type SubscribedComponent = React.AbstractComponent<
|
||||
$ReadOnly<{|
|
||||
logs: $ReadOnlyArray<LogBoxLog>,
|
||||
isDisabled: boolean,
|
||||
selectedLogIndex: number,
|
||||
|}>,
|
||||
>;
|
||||
|
||||
export function withSubscription(
|
||||
WrappedComponent: SubscribedComponent,
|
||||
): React.AbstractComponent<{||}> {
|
||||
class LogBoxStateSubscription extends React.Component<Props, State> {
|
||||
static getDerivedStateFromError() {
|
||||
return {hasError: true};
|
||||
}
|
||||
|
||||
componentDidCatch(err: Error, errorInfo: {componentStack: string, ...}) {
|
||||
reportLogBoxError(err, errorInfo.componentStack);
|
||||
}
|
||||
|
||||
_subscription: ?Subscription;
|
||||
|
||||
state = {
|
||||
logs: new Set(),
|
||||
isDisabled: false,
|
||||
hasError: false,
|
||||
selectedLogIndex: -1,
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
if (this.state.hasError) {
|
||||
// This happens when the component failed to render, in which case we delegate to the native redbox.
|
||||
// We can't show anyback fallback UI here, because the error may be with <View> or <Text>.
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<WrappedComponent
|
||||
logs={Array.from(this.state.logs)}
|
||||
isDisabled={this.state.isDisabled}
|
||||
selectedLogIndex={this.state.selectedLogIndex}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this._subscription = observe(data => {
|
||||
this.setState(data);
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (this._subscription != null) {
|
||||
this._subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
_handleDismiss = (): void => {
|
||||
// Here we handle the cases when the log is dismissed and it
|
||||
// was either the last log, or when the current index
|
||||
// is now outside the bounds of the log array.
|
||||
const {selectedLogIndex, logs: stateLogs} = this.state;
|
||||
const logsArray = Array.from(stateLogs);
|
||||
if (selectedLogIndex != null) {
|
||||
if (logsArray.length - 1 <= 0) {
|
||||
setSelectedLog(-1);
|
||||
} else if (selectedLogIndex >= logsArray.length - 1) {
|
||||
setSelectedLog(selectedLogIndex - 1);
|
||||
}
|
||||
|
||||
dismiss(logsArray[selectedLogIndex]);
|
||||
}
|
||||
};
|
||||
|
||||
_handleMinimize = (): void => {
|
||||
setSelectedLog(-1);
|
||||
};
|
||||
|
||||
_handleSetSelectedLog = (index: number): void => {
|
||||
setSelectedLog(index);
|
||||
};
|
||||
}
|
||||
|
||||
return LogBoxStateSubscription;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,13 @@ function getLogBoxLog() {
|
||||
message: {content: '...', substitutions: []},
|
||||
stack: createStack(['A', 'B', 'C']),
|
||||
category: 'Message category...',
|
||||
componentStack: [{component: 'LogBoxLog', location: 'LogBoxLog.js:1'}],
|
||||
componentStack: [
|
||||
{
|
||||
content: 'LogBoxLog',
|
||||
fileName: 'LogBoxLog.js',
|
||||
location: {column: -1, row: 1},
|
||||
},
|
||||
],
|
||||
codeFrame: {
|
||||
fileName: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js',
|
||||
location: {row: 199, column: 0},
|
||||
@@ -69,7 +75,11 @@ describe('LogBoxLog', () => {
|
||||
expect(log.stack).toEqual(createStack(['A', 'B', 'C']));
|
||||
expect(log.category).toEqual('Message category...');
|
||||
expect(log.componentStack).toEqual([
|
||||
{component: 'LogBoxLog', location: 'LogBoxLog.js:1'},
|
||||
{
|
||||
content: 'LogBoxLog',
|
||||
fileName: 'LogBoxLog.js',
|
||||
location: {column: -1, row: 1},
|
||||
},
|
||||
]);
|
||||
expect(log.codeFrame).toEqual({
|
||||
fileName: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js',
|
||||
|
||||
@@ -117,8 +117,16 @@ describe('parseLogBoxLog', () => {
|
||||
]),
|
||||
).toEqual({
|
||||
componentStack: [
|
||||
{component: 'MyComponent', location: 'filename.js:1'},
|
||||
{component: 'MyOtherComponent', location: 'filename2.js:1'},
|
||||
{
|
||||
content: 'MyComponent',
|
||||
fileName: 'filename.js',
|
||||
location: {column: -1, row: 1},
|
||||
},
|
||||
{
|
||||
content: 'MyOtherComponent',
|
||||
fileName: 'filename2.js',
|
||||
location: {column: -1, row: 1},
|
||||
},
|
||||
],
|
||||
category:
|
||||
'Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?%s',
|
||||
@@ -143,8 +151,16 @@ describe('parseLogBoxLog', () => {
|
||||
]),
|
||||
).toEqual({
|
||||
componentStack: [
|
||||
{component: 'MyComponent', location: 'filename.js:1'},
|
||||
{component: 'MyOtherComponent', location: 'filename2.js:1'},
|
||||
{
|
||||
content: 'MyComponent',
|
||||
fileName: 'filename.js',
|
||||
location: {column: -1, row: 1},
|
||||
},
|
||||
{
|
||||
content: 'MyOtherComponent',
|
||||
fileName: 'filename2.js',
|
||||
location: {column: -1, row: 1},
|
||||
},
|
||||
],
|
||||
category: 'Some kind of message',
|
||||
message: {
|
||||
@@ -164,8 +180,16 @@ describe('parseLogBoxLog', () => {
|
||||
]),
|
||||
).toEqual({
|
||||
componentStack: [
|
||||
{component: 'MyComponent', location: 'filename.js:1'},
|
||||
{component: 'MyOtherComponent', location: 'filename2.js:1'},
|
||||
{
|
||||
content: 'MyComponent',
|
||||
fileName: 'filename.js',
|
||||
location: {column: -1, row: 1},
|
||||
},
|
||||
{
|
||||
content: 'MyOtherComponent',
|
||||
fileName: 'filename2.js',
|
||||
location: {column: -1, row: 1},
|
||||
},
|
||||
],
|
||||
category:
|
||||
'Some kind of message Some other kind of message Some third kind of message',
|
||||
@@ -418,12 +442,14 @@ Please follow the instructions at: fburl.com/rn-remote-assets`,
|
||||
},
|
||||
componentStack: [
|
||||
{
|
||||
component: 'MyComponent',
|
||||
location: 'filename.js:1',
|
||||
content: 'MyComponent',
|
||||
fileName: 'filename.js',
|
||||
location: {column: -1, row: 1},
|
||||
},
|
||||
{
|
||||
component: 'MyOtherComponent',
|
||||
location: 'filename2.js:1',
|
||||
content: 'MyOtherComponent',
|
||||
fileName: 'filename2.js',
|
||||
location: {column: -1, row: 1},
|
||||
},
|
||||
],
|
||||
stack: [
|
||||
|
||||
@@ -42,12 +42,7 @@ export type Message = $ReadOnly<{|
|
||||
>,
|
||||
|}>;
|
||||
|
||||
export type ComponentStack = $ReadOnlyArray<
|
||||
$ReadOnly<{|
|
||||
component: string,
|
||||
location: string,
|
||||
|}>,
|
||||
>;
|
||||
export type ComponentStack = $ReadOnlyArray<CodeFrame>;
|
||||
|
||||
const SUBSTITUTION = UTFSequence.BOM + '%s';
|
||||
|
||||
@@ -127,6 +122,7 @@ export function parseCategory(
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function parseComponentStack(message: string): ComponentStack {
|
||||
return message
|
||||
.split(/\n {4}in /g)
|
||||
@@ -134,11 +130,17 @@ export function parseComponentStack(message: string): ComponentStack {
|
||||
if (!s) {
|
||||
return null;
|
||||
}
|
||||
let [component, location] = s.split(/ \(at /);
|
||||
if (!location) {
|
||||
[component, location] = s.split(/ \(/);
|
||||
const match = s.match(/(.*) \(at (.*\.js):([\d]+)\)/);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
return {component, location: location && location.replace(')', '')};
|
||||
|
||||
let [content, fileName, row] = match.slice(1);
|
||||
return {
|
||||
content,
|
||||
fileName,
|
||||
location: {column: -1, row: parseInt(row, 10)},
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
+28
-98
@@ -10,26 +10,14 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as React from 'react';
|
||||
import Platform from '../Utilities/Platform';
|
||||
import RCTLog from '../Utilities/RCTLog';
|
||||
import LogBoxContainer from './UI/LogBoxContainer';
|
||||
import * as LogBoxData from './Data/LogBoxData';
|
||||
import {parseLogBoxLog} from './Data/parseLogBoxLog';
|
||||
|
||||
import type {LogBoxLogs, Subscription, IgnorePattern} from './Data/LogBoxData';
|
||||
import type {IgnorePattern} from './Data/LogBoxData';
|
||||
|
||||
import LogBoxLog from './Data/LogBoxLog';
|
||||
|
||||
type Props = $ReadOnly<{||}>;
|
||||
type State = {|
|
||||
logs: LogBoxLogs,
|
||||
isDisabled: boolean,
|
||||
hasError: boolean,
|
||||
selectedLogIndex: number,
|
||||
|};
|
||||
|
||||
let LogBoxComponent;
|
||||
let LogBox;
|
||||
|
||||
/**
|
||||
* LogBox displays logs in the app.
|
||||
@@ -48,25 +36,26 @@ if (__DEV__) {
|
||||
warnImpl(...args);
|
||||
};
|
||||
|
||||
LogBoxComponent = class LogBox extends React.Component<Props, State> {
|
||||
static getDerivedStateFromError() {
|
||||
return {hasError: true};
|
||||
}
|
||||
|
||||
componentDidCatch(err, errorInfo) {
|
||||
LogBoxData.reportLogBoxError(err, errorInfo.componentStack);
|
||||
}
|
||||
|
||||
LogBox = {
|
||||
// TODO: deprecated, replace with ignoreLogs
|
||||
static ignoreWarnings(patterns: $ReadOnlyArray<IgnorePattern>): void {
|
||||
ignoreWarnings: (patterns: $ReadOnlyArray<IgnorePattern>): void => {
|
||||
LogBox.ignoreLogs(patterns);
|
||||
}
|
||||
},
|
||||
|
||||
static ignoreLogs(patterns: $ReadOnlyArray<IgnorePattern>): void {
|
||||
ignoreLogs: (patterns: $ReadOnlyArray<IgnorePattern>): void => {
|
||||
LogBoxData.addIgnorePatterns(patterns);
|
||||
}
|
||||
},
|
||||
|
||||
uninstall: (): void => {
|
||||
errorImpl = error;
|
||||
warnImpl = warn;
|
||||
delete (console: any).disableLogBox;
|
||||
},
|
||||
|
||||
install: (): void => {
|
||||
// Trigger lazy initialization of module.
|
||||
require('../NativeModules/specs/NativeLogBox');
|
||||
|
||||
static install(): void {
|
||||
errorImpl = function(...args) {
|
||||
registerError(...args);
|
||||
};
|
||||
@@ -92,62 +81,7 @@ if (__DEV__) {
|
||||
RCTLog.setWarningHandler((...args) => {
|
||||
registerWarning(...args);
|
||||
});
|
||||
}
|
||||
|
||||
static uninstall(): void {
|
||||
errorImpl = error;
|
||||
warnImpl = warn;
|
||||
delete (console: any).disableLogBox;
|
||||
}
|
||||
|
||||
_subscription: ?Subscription;
|
||||
|
||||
state = {
|
||||
logs: new Set(),
|
||||
isDisabled: false,
|
||||
hasError: false,
|
||||
selectedLogIndex: -1,
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
if (this.state.hasError) {
|
||||
// This happens when the component failed to render, in which case we delegate to the native redbox.
|
||||
// We can't show anyback fallback UI here, because the error may be with <View> or <Text>.
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.state.logs == null ? null : (
|
||||
<LogBoxContainer
|
||||
onDismiss={this._handleDismiss}
|
||||
onDismissWarns={LogBoxData.clearWarnings}
|
||||
onDismissErrors={LogBoxData.clearErrors}
|
||||
logs={this.state.logs}
|
||||
isDisabled={this.state.isDisabled}
|
||||
selectedLogIndex={this.state.selectedLogIndex}
|
||||
setSelectedLog={this._handleSetSelectedLog}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this._subscription = LogBoxData.observe(data => {
|
||||
this.setState(data);
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (this._subscription != null) {
|
||||
this._subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
_handleDismiss(log: LogBoxLog): void {
|
||||
LogBoxData.dismiss(log);
|
||||
}
|
||||
|
||||
_handleSetSelectedLog(index: number): void {
|
||||
LogBoxData.setSelectedLog(index);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const isRCTLogAdviceWarning = (...args) => {
|
||||
@@ -227,31 +161,27 @@ if (__DEV__) {
|
||||
}
|
||||
};
|
||||
} else {
|
||||
LogBoxComponent = class extends React.Component<Props, State> {
|
||||
LogBox = {
|
||||
// TODO: deprecated, replace with ignoreLogs
|
||||
static ignoreWarnings(patterns: $ReadOnlyArray<IgnorePattern>): void {
|
||||
ignoreWarnings: (patterns: $ReadOnlyArray<IgnorePattern>): void => {
|
||||
// Do nothing.
|
||||
}
|
||||
},
|
||||
|
||||
static ignoreLogs(patterns: $ReadOnlyArray<IgnorePattern>): void {
|
||||
ignoreLogs: (patterns: $ReadOnlyArray<IgnorePattern>): void => {
|
||||
// Do nothing.
|
||||
}
|
||||
},
|
||||
|
||||
static install(): void {
|
||||
install: (): void => {
|
||||
// Do nothing.
|
||||
}
|
||||
},
|
||||
|
||||
static uninstall(): void {
|
||||
uninstall: (): void => {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = (LogBoxComponent: Class<React.Component<Props, State>> & {
|
||||
module.exports = (LogBox: {
|
||||
// TODO: deprecated, replace with ignoreLogs
|
||||
ignoreWarnings($ReadOnlyArray<IgnorePattern>): void,
|
||||
ignoreLogs($ReadOnlyArray<IgnorePattern>): void,
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as React from 'react';
|
||||
import {View, StyleSheet} from 'react-native';
|
||||
import * as LogBoxData from './Data/LogBoxData';
|
||||
import LogBoxInspector from './UI/LogBoxInspector';
|
||||
import type LogBoxLog from './Data/LogBoxLog';
|
||||
import NativeLogBox from '../NativeModules/specs/NativeLogBox';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
logs: $ReadOnlyArray<LogBoxLog>,
|
||||
selectedLogIndex: number,
|
||||
isDisabled?: ?boolean,
|
||||
|}>;
|
||||
|
||||
function NativeLogBoxVisibility(props) {
|
||||
React.useLayoutEffect(() => {
|
||||
if (NativeLogBox) {
|
||||
if (props.visible) {
|
||||
// Schedule this to try and prevent flashing the old state.
|
||||
setTimeout(() => NativeLogBox.show(), 10);
|
||||
} else {
|
||||
NativeLogBox.hide();
|
||||
}
|
||||
}
|
||||
}, [props.visible]);
|
||||
|
||||
return props.children;
|
||||
}
|
||||
|
||||
export class _LogBoxInspectorContainer extends React.Component<Props> {
|
||||
render(): React.Node {
|
||||
return (
|
||||
<NativeLogBoxVisibility visible={this.props.selectedLogIndex >= 0}>
|
||||
<View style={StyleSheet.absoluteFill}>
|
||||
<LogBoxInspector
|
||||
onDismiss={this._handleDismiss}
|
||||
onMinimize={this._handleMinimize}
|
||||
onChangeSelectedIndex={this._handleSetSelectedLog}
|
||||
logs={this.props.logs}
|
||||
selectedIndex={this.props.selectedLogIndex}
|
||||
/>
|
||||
</View>
|
||||
</NativeLogBoxVisibility>
|
||||
);
|
||||
}
|
||||
|
||||
_handleDismiss = (): void => {
|
||||
// Here we handle the cases when the log is dismissed and it
|
||||
// was either the last log, or when the current index
|
||||
// is now outside the bounds of the log array.
|
||||
const {selectedLogIndex, logs} = this.props;
|
||||
const logsArray = Array.from(logs);
|
||||
if (selectedLogIndex != null) {
|
||||
if (logsArray.length - 1 <= 0) {
|
||||
LogBoxData.setSelectedLog(-1);
|
||||
} else if (selectedLogIndex >= logsArray.length - 1) {
|
||||
LogBoxData.setSelectedLog(selectedLogIndex - 1);
|
||||
}
|
||||
|
||||
LogBoxData.dismiss(logsArray[selectedLogIndex]);
|
||||
}
|
||||
};
|
||||
|
||||
_handleMinimize = (): void => {
|
||||
LogBoxData.setSelectedLog(-1);
|
||||
};
|
||||
|
||||
_handleSetSelectedLog = (index: number): void => {
|
||||
LogBoxData.setSelectedLog(index);
|
||||
};
|
||||
}
|
||||
|
||||
export default (LogBoxData.withSubscription(
|
||||
_LogBoxInspectorContainer,
|
||||
): React.AbstractComponent<{||}>);
|
||||
+22
-49
@@ -11,46 +11,31 @@
|
||||
'use strict';
|
||||
|
||||
import * as React from 'react';
|
||||
import SafeAreaView from '../../Components/SafeAreaView/SafeAreaView';
|
||||
import StyleSheet from '../../StyleSheet/StyleSheet';
|
||||
import View from '../../Components/View/View';
|
||||
import LogBoxInspector from './LogBoxInspector';
|
||||
import LogBoxLog from '../Data/LogBoxLog';
|
||||
import LogBoxLogNotification from './LogBoxLogNotification';
|
||||
import type {LogBoxLogs} from '../Data/LogBoxData';
|
||||
import StyleSheet from '../StyleSheet/StyleSheet';
|
||||
import View from '../Components/View/View';
|
||||
import * as LogBoxData from './Data/LogBoxData';
|
||||
import LogBoxLog from './Data/LogBoxLog';
|
||||
import LogBoxLogNotification from './UI/LogBoxNotification';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
onDismiss: (log: LogBoxLog) => void,
|
||||
onDismissWarns: () => void,
|
||||
onDismissErrors: () => void,
|
||||
setSelectedLog: number => void,
|
||||
logs: LogBoxLogs,
|
||||
logs: $ReadOnlyArray<LogBoxLog>,
|
||||
selectedLogIndex: number,
|
||||
isDisabled?: ?boolean,
|
||||
|}>;
|
||||
|
||||
function LogBoxContainer(props: Props): React.Node {
|
||||
const {selectedLogIndex, setSelectedLog} = props;
|
||||
export function _LogBoxNotificationContainer(props: Props): React.Node {
|
||||
const {logs} = props;
|
||||
|
||||
const logs = Array.from(props.logs);
|
||||
const onDismissWarns = () => {
|
||||
LogBoxData.clearWarnings();
|
||||
};
|
||||
const onDismissErrors = () => {
|
||||
LogBoxData.clearErrors();
|
||||
};
|
||||
|
||||
function handleInspectorDismiss() {
|
||||
// Here we handle the cases when the log is dismissed and it
|
||||
// was either the last log, or when the current index
|
||||
// is now outside the bounds of the log array.
|
||||
if (selectedLogIndex != null) {
|
||||
if (logs.length - 1 <= 0) {
|
||||
setSelectedLog(-1);
|
||||
} else if (selectedLogIndex >= logs.length - 1) {
|
||||
setSelectedLog(selectedLogIndex - 1);
|
||||
}
|
||||
props.onDismiss(logs[selectedLogIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
function handleInspectorMinimize() {
|
||||
setSelectedLog(-1);
|
||||
}
|
||||
const setSelectedLog = (index: number): void => {
|
||||
LogBoxData.setSelectedLog(index);
|
||||
};
|
||||
|
||||
function openLog(log: LogBoxLog) {
|
||||
let index = logs.length - 1;
|
||||
@@ -62,20 +47,6 @@ function LogBoxContainer(props: Props): React.Node {
|
||||
setSelectedLog(index);
|
||||
}
|
||||
|
||||
if (selectedLogIndex > -1) {
|
||||
return (
|
||||
<View style={StyleSheet.absoluteFill}>
|
||||
<LogBoxInspector
|
||||
onDismiss={handleInspectorDismiss}
|
||||
onMinimize={handleInspectorMinimize}
|
||||
onChangeSelectedIndex={setSelectedLog}
|
||||
logs={logs}
|
||||
selectedIndex={selectedLogIndex}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
if (logs.length === 0 || props.isDisabled === true) {
|
||||
return null;
|
||||
}
|
||||
@@ -93,7 +64,7 @@ function LogBoxContainer(props: Props): React.Node {
|
||||
level="warn"
|
||||
totalLogCount={warnings.length}
|
||||
onPressOpen={() => openLog(warnings[warnings.length - 1])}
|
||||
onPressDismiss={props.onDismissWarns}
|
||||
onPressDismiss={onDismissWarns}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
@@ -104,7 +75,7 @@ function LogBoxContainer(props: Props): React.Node {
|
||||
level="error"
|
||||
totalLogCount={errors.length}
|
||||
onPressOpen={() => openLog(errors[errors.length - 1])}
|
||||
onPressDismiss={props.onDismissErrors}
|
||||
onPressDismiss={onDismissErrors}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
@@ -126,4 +97,6 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
});
|
||||
|
||||
export default LogBoxContainer;
|
||||
export default (LogBoxData.withSubscription(
|
||||
_LogBoxNotificationContainer,
|
||||
): React.AbstractComponent<{||}>);
|
||||
@@ -14,7 +14,7 @@ import LogBoxInspectorCodeFrame from './LogBoxInspectorCodeFrame';
|
||||
import * as React from 'react';
|
||||
import ScrollView from '../../Components/ScrollView/ScrollView';
|
||||
import StyleSheet from '../../StyleSheet/StyleSheet';
|
||||
import Modal from '../../Modal/Modal';
|
||||
import View from '../../Components/View/View';
|
||||
import * as LogBoxData from '../Data/LogBoxData';
|
||||
import Keyboard from '../../Components/Keyboard/Keyboard';
|
||||
import LogBoxInspectorFooter from './LogBoxInspectorFooter';
|
||||
@@ -23,8 +23,7 @@ import LogBoxInspectorReactFrames from './LogBoxInspectorReactFrames';
|
||||
import LogBoxInspectorStackFrames from './LogBoxInspectorStackFrames';
|
||||
import LogBoxInspectorHeader from './LogBoxInspectorHeader';
|
||||
import * as LogBoxStyle from './LogBoxStyle';
|
||||
|
||||
import type LogBoxLog, {LogLevel} from '../Data/LogBoxLog';
|
||||
import LogBoxLog, {type LogLevel} from '../Data/LogBoxLog';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
onDismiss: () => void,
|
||||
@@ -40,7 +39,9 @@ function LogBoxInspector(props: Props): React.Node {
|
||||
|
||||
const log = logs[selectedIndex];
|
||||
React.useEffect(() => {
|
||||
LogBoxData.symbolicateLogNow(log);
|
||||
if (log) {
|
||||
LogBoxData.symbolicateLogNow(log);
|
||||
}
|
||||
}, [log]);
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -68,12 +69,7 @@ function LogBoxInspector(props: Props): React.Node {
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
animationType="none"
|
||||
visible
|
||||
statusBarTranslucent
|
||||
supportedOrientations={['portrait']}
|
||||
presentationStyle="overFullScreen">
|
||||
<View style={styles.root}>
|
||||
<LogBoxInspectorHeader
|
||||
onSelectIndex={props.onChangeSelectedIndex}
|
||||
selectedIndex={selectedIndex}
|
||||
@@ -86,7 +82,7 @@ function LogBoxInspector(props: Props): React.Node {
|
||||
onMinimize={props.onMinimize}
|
||||
level={log.level}
|
||||
/>
|
||||
</Modal>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -143,6 +139,10 @@ function LogBoxInspectorBody(props) {
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
root: {
|
||||
flex: 1,
|
||||
backgroundColor: LogBoxStyle.getTextColor(),
|
||||
},
|
||||
scrollBody: {
|
||||
backgroundColor: LogBoxStyle.getBackgroundColor(0.9),
|
||||
flex: 1,
|
||||
|
||||
@@ -126,7 +126,7 @@ const headerStyles = StyleSheet.create({
|
||||
borderRadius: 3,
|
||||
},
|
||||
buttonImage: {
|
||||
tintColor: LogBoxStyle.getBackgroundColor(1),
|
||||
tintColor: LogBoxStyle.getTextColor(),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -156,7 +156,7 @@ const styles = StyleSheet.create({
|
||||
justifyContent: 'center',
|
||||
},
|
||||
titleText: {
|
||||
color: LogBoxStyle.getBackgroundColor(1),
|
||||
color: LogBoxStyle.getTextColor(),
|
||||
fontSize: 16,
|
||||
fontWeight: '600',
|
||||
includeFontPadding: false,
|
||||
|
||||
@@ -18,12 +18,37 @@ import View from '../../Components/View/View';
|
||||
import LogBoxButton from './LogBoxButton';
|
||||
import * as LogBoxStyle from './LogBoxStyle';
|
||||
import LogBoxInspectorSection from './LogBoxInspectorSection';
|
||||
import openFileInEditor from '../../Core/Devtools/openFileInEditor';
|
||||
import type LogBoxLog from '../Data/LogBoxLog';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
log: LogBoxLog,
|
||||
|}>;
|
||||
|
||||
const BEFORE_SLASH_RE = /^(.*)[\\/]/;
|
||||
|
||||
// Taken from React https://github.com/facebook/react/blob/206d61f72214e8ae5b935f0bf8628491cb7f0797/packages/react-devtools-shared/src/backend/describeComponentFrame.js#L27-L41
|
||||
function getPrettyFileName(path) {
|
||||
let fileName = path.replace(BEFORE_SLASH_RE, '');
|
||||
|
||||
// In DEV, include code for a common special case:
|
||||
// prefer "folder/index.js" instead of just "index.js".
|
||||
if (/^index\./.test(fileName)) {
|
||||
const match = path.match(BEFORE_SLASH_RE);
|
||||
if (match) {
|
||||
const pathBeforeSlash = match[1];
|
||||
if (pathBeforeSlash) {
|
||||
const folderName = pathBeforeSlash.replace(BEFORE_SLASH_RE, '');
|
||||
// Note the below string contains a zero width space after the "/" character.
|
||||
// This is to prevent browsers like Chrome from formatting the file name as a link.
|
||||
// (Since this is a source link, it would not work to open the source file anyway.)
|
||||
fileName = folderName + '/' + fileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fileName;
|
||||
}
|
||||
function LogBoxInspectorReactFrames(props: Props): React.Node {
|
||||
const [collapsed, setCollapsed] = React.useState(true);
|
||||
if (props.log.componentStack == null || props.log.componentStack.length < 1) {
|
||||
@@ -39,6 +64,10 @@ function LogBoxInspectorReactFrames(props: Props): React.Node {
|
||||
}
|
||||
|
||||
function getCollapseMessage() {
|
||||
if (props.log.componentStack.length <= 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
const count = props.log.componentStack.length - 3;
|
||||
if (collapsed) {
|
||||
return `See ${count} more components`;
|
||||
@@ -53,15 +82,34 @@ function LogBoxInspectorReactFrames(props: Props): React.Node {
|
||||
<View
|
||||
// Unfortunately we don't have a unique identifier for stack traces.
|
||||
key={index}
|
||||
style={componentStyles.frame}>
|
||||
<View style={componentStyles.component}>
|
||||
<Text style={componentStyles.frameName}>
|
||||
<Text style={componentStyles.bracket}>{'<'}</Text>
|
||||
{frame.component}
|
||||
<Text style={componentStyles.bracket}>{' />'}</Text>
|
||||
style={componentStyles.frameContainer}>
|
||||
<LogBoxButton
|
||||
backgroundColor={{
|
||||
default: 'transparent',
|
||||
pressed: LogBoxStyle.getBackgroundColor(1),
|
||||
}}
|
||||
onPress={
|
||||
// Older versions of DevTools do not provide full path.
|
||||
// This will not work on Windows, remove check once the
|
||||
// DevTools return the full file path.
|
||||
frame.fileName.startsWith('/')
|
||||
? () =>
|
||||
openFileInEditor(frame.fileName, frame.location?.row ?? 1)
|
||||
: null
|
||||
}
|
||||
style={componentStyles.frame}>
|
||||
<View style={componentStyles.component}>
|
||||
<Text style={componentStyles.frameName}>
|
||||
<Text style={componentStyles.bracket}>{'<'}</Text>
|
||||
{frame.content}
|
||||
<Text style={componentStyles.bracket}>{' />'}</Text>
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={componentStyles.frameLocation}>
|
||||
{getPrettyFileName(frame.fileName)}
|
||||
{frame.location ? `:${frame.location.row}` : ''}
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={componentStyles.frameLocation}>{frame.location}</Text>
|
||||
</LogBoxButton>
|
||||
</View>
|
||||
))}
|
||||
<View style={componentStyles.collapseContainer}>
|
||||
@@ -96,9 +144,15 @@ const componentStyles = StyleSheet.create({
|
||||
paddingVertical: 5,
|
||||
paddingHorizontal: 10,
|
||||
},
|
||||
frameContainer: {
|
||||
flexDirection: 'row',
|
||||
paddingHorizontal: 15,
|
||||
},
|
||||
frame: {
|
||||
paddingHorizontal: 25,
|
||||
flex: 1,
|
||||
paddingVertical: 4,
|
||||
paddingHorizontal: 10,
|
||||
borderRadius: 5,
|
||||
},
|
||||
component: {
|
||||
flexDirection: 'row',
|
||||
|
||||
@@ -1,254 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @emails oncall+react_native
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const LogBoxContainer = require('../LogBoxContainer').default;
|
||||
const LogBoxLog = require('../../Data/LogBoxLog').default;
|
||||
const render = require('../../../../jest/renderer');
|
||||
|
||||
describe('LogBoxContainer', () => {
|
||||
it('should render null with no logs', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxContainer
|
||||
onDismiss={() => {}}
|
||||
onDismissWarns={() => {}}
|
||||
onDismissErrors={() => {}}
|
||||
setSelectedLog={() => {}}
|
||||
selectedLogIndex={-1}
|
||||
logs={new Set()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render null with no selected log and disabled', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxContainer
|
||||
isDisabled
|
||||
onDismiss={() => {}}
|
||||
onDismissWarns={() => {}}
|
||||
onDismissErrors={() => {}}
|
||||
setSelectedLog={() => {}}
|
||||
selectedLogIndex={-1}
|
||||
logs={
|
||||
new Set([
|
||||
new LogBoxLog({
|
||||
level: 'warn',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message',
|
||||
componentStack: [],
|
||||
}),
|
||||
])
|
||||
}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render the latest warning notification', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxContainer
|
||||
onDismiss={() => {}}
|
||||
onDismissWarns={() => {}}
|
||||
onDismissErrors={() => {}}
|
||||
setSelectedLog={() => {}}
|
||||
selectedLogIndex={-1}
|
||||
logs={
|
||||
new Set([
|
||||
new LogBoxLog({
|
||||
level: 'warn',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message',
|
||||
componentStack: [],
|
||||
}),
|
||||
new LogBoxLog({
|
||||
level: 'warn',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message (latest)',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message (latest)',
|
||||
componentStack: [],
|
||||
}),
|
||||
])
|
||||
}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render the latest error notification', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxContainer
|
||||
onDismiss={() => {}}
|
||||
onDismissWarns={() => {}}
|
||||
onDismissErrors={() => {}}
|
||||
setSelectedLog={() => {}}
|
||||
selectedLogIndex={-1}
|
||||
logs={
|
||||
new Set([
|
||||
new LogBoxLog({
|
||||
level: 'error',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message',
|
||||
componentStack: [],
|
||||
}),
|
||||
new LogBoxLog({
|
||||
level: 'error',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message (latest)',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message (latest)',
|
||||
componentStack: [],
|
||||
}),
|
||||
])
|
||||
}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render both an error and warning notification', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxContainer
|
||||
onDismiss={() => {}}
|
||||
onDismissWarns={() => {}}
|
||||
onDismissErrors={() => {}}
|
||||
setSelectedLog={() => {}}
|
||||
selectedLogIndex={-1}
|
||||
logs={
|
||||
new Set([
|
||||
new LogBoxLog({
|
||||
level: 'warn',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message',
|
||||
componentStack: [],
|
||||
}),
|
||||
new LogBoxLog({
|
||||
level: 'error',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message (latest)',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message (latest)',
|
||||
componentStack: [],
|
||||
}),
|
||||
])
|
||||
}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render selected fatal error even when disabled', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxContainer
|
||||
isDisabled
|
||||
onDismiss={() => {}}
|
||||
onDismissWarns={() => {}}
|
||||
onDismissErrors={() => {}}
|
||||
setSelectedLog={() => {}}
|
||||
selectedLogIndex={0}
|
||||
logs={
|
||||
new Set([
|
||||
new LogBoxLog({
|
||||
level: 'fatal',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Should be selected',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message',
|
||||
componentStack: [],
|
||||
}),
|
||||
])
|
||||
}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render selected syntax error even when disabled', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxContainer
|
||||
isDisabled
|
||||
onDismiss={() => {}}
|
||||
onDismissWarns={() => {}}
|
||||
onDismissErrors={() => {}}
|
||||
setSelectedLog={() => {}}
|
||||
selectedLogIndex={0}
|
||||
logs={
|
||||
new Set([
|
||||
new LogBoxLog({
|
||||
level: 'syntax',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Should be selected',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of syntax error message',
|
||||
componentStack: [],
|
||||
codeFrame: {
|
||||
fileName:
|
||||
'/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js',
|
||||
location: {row: 199, column: 0},
|
||||
content: ` 197 | });
|
||||
198 |
|
||||
> 199 | export default CrashReactApp;
|
||||
| ^
|
||||
200 |`,
|
||||
},
|
||||
}),
|
||||
])
|
||||
}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -40,7 +40,7 @@ describe('LogBoxInspectorReactFrames', () => {
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render componentStack frames', () => {
|
||||
it('should render componentStack frames without full path pressable', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxInspectorReactFrames
|
||||
log={
|
||||
@@ -55,8 +55,129 @@ describe('LogBoxInspectorReactFrames', () => {
|
||||
category: 'Some kind of message',
|
||||
componentStack: [
|
||||
{
|
||||
component: 'MyComponent',
|
||||
location: 'MyComponentFile.js:1',
|
||||
content: 'MyComponent',
|
||||
fileName: 'MyComponentFile.js',
|
||||
location: {
|
||||
row: 1,
|
||||
column: -1,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render componentStack frames with full path pressable', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxInspectorReactFrames
|
||||
log={
|
||||
new LogBoxLog({
|
||||
level: 'warn',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message',
|
||||
componentStack: [
|
||||
{
|
||||
content: 'MyComponent',
|
||||
fileName: '/path/to/MyComponentFile.js',
|
||||
location: {
|
||||
row: 1,
|
||||
column: -1,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render componentStack frames with parent folder of index.js', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxInspectorReactFrames
|
||||
log={
|
||||
new LogBoxLog({
|
||||
level: 'warn',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message',
|
||||
componentStack: [
|
||||
{
|
||||
content: 'MyComponent',
|
||||
fileName: '/path/to/index.js',
|
||||
location: {
|
||||
row: 1,
|
||||
column: -1,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render componentStack frames with more than 3 stacks', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxInspectorReactFrames
|
||||
log={
|
||||
new LogBoxLog({
|
||||
level: 'warn',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message',
|
||||
componentStack: [
|
||||
{
|
||||
content: 'MyComponent',
|
||||
fileName: '/path/to/index.js',
|
||||
location: {
|
||||
row: 1,
|
||||
column: -1,
|
||||
},
|
||||
},
|
||||
{
|
||||
content: 'MyComponent2',
|
||||
fileName: '/path/to/index2.js',
|
||||
location: {
|
||||
row: 1,
|
||||
column: -1,
|
||||
},
|
||||
},
|
||||
{
|
||||
content: 'MyComponent3',
|
||||
fileName: '/path/to/index3.js',
|
||||
location: {
|
||||
row: 1,
|
||||
column: -1,
|
||||
},
|
||||
},
|
||||
{
|
||||
content: 'MyComponent4',
|
||||
fileName: '/path/to/index4.js',
|
||||
location: {
|
||||
row: 1,
|
||||
column: -1,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
+3
-3
@@ -12,7 +12,7 @@
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const LogBoxLogNotification = require('../LogBoxLogNotification').default;
|
||||
const LogBoxNotification = require('../LogBoxNotification').default;
|
||||
const LogBoxLog = require('../../Data/LogBoxLog').default;
|
||||
const render = require('../../../../jest/renderer');
|
||||
|
||||
@@ -28,10 +28,10 @@ const log = new LogBoxLog({
|
||||
componentStack: [],
|
||||
});
|
||||
|
||||
describe('LogBoxLogNotification', () => {
|
||||
describe('LogBoxNotification', () => {
|
||||
it('should render log', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxLogNotification
|
||||
<LogBoxNotification
|
||||
log={log}
|
||||
totalLogCount={1}
|
||||
level="warn"
|
||||
@@ -1,17 +1,13 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`LogBoxContainer should render fatal with selectedIndex 2 1`] = `
|
||||
<Component
|
||||
animationType="none"
|
||||
hardwareAccelerated={false}
|
||||
presentationStyle="overFullScreen"
|
||||
statusBarTranslucent={true}
|
||||
supportedOrientations={
|
||||
Array [
|
||||
"portrait",
|
||||
]
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(255, 255, 255, 1)",
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
visible={true}
|
||||
>
|
||||
<LogBoxInspectorHeader
|
||||
level="fatal"
|
||||
@@ -47,23 +43,19 @@ exports[`LogBoxContainer should render fatal with selectedIndex 2 1`] = `
|
||||
onDismiss={[Function]}
|
||||
onMinimize={[Function]}
|
||||
/>
|
||||
</Component>
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`LogBoxContainer should render null with no logs 1`] = `null`;
|
||||
|
||||
exports[`LogBoxContainer should render warning with selectedIndex 0 1`] = `
|
||||
<Component
|
||||
animationType="none"
|
||||
hardwareAccelerated={false}
|
||||
presentationStyle="overFullScreen"
|
||||
statusBarTranslucent={true}
|
||||
supportedOrientations={
|
||||
Array [
|
||||
"portrait",
|
||||
]
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"backgroundColor": "rgba(255, 255, 255, 1)",
|
||||
"flex": 1,
|
||||
}
|
||||
}
|
||||
visible={true}
|
||||
>
|
||||
<LogBoxInspectorHeader
|
||||
level="warn"
|
||||
@@ -99,5 +91,5 @@ exports[`LogBoxContainer should render warning with selectedIndex 0 1`] = `
|
||||
onDismiss={[Function]}
|
||||
onMinimize={[Function]}
|
||||
/>
|
||||
</Component>
|
||||
</View>
|
||||
`;
|
||||
|
||||
@@ -39,7 +39,7 @@ exports[`LogBoxInspectorHeader should render both buttons for two total 1`] = `
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(51, 51, 51, 1)",
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "600",
|
||||
"includeFontPadding": false,
|
||||
@@ -99,7 +99,7 @@ exports[`LogBoxInspectorHeader should render no buttons for one total 1`] = `
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(51, 51, 51, 1)",
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "600",
|
||||
"includeFontPadding": false,
|
||||
@@ -153,7 +153,7 @@ exports[`LogBoxInspectorHeader should render syntax error header 1`] = `
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(51, 51, 51, 1)",
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "600",
|
||||
"includeFontPadding": false,
|
||||
@@ -207,7 +207,7 @@ exports[`LogBoxInspectorHeader should render two buttons for three or more total
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(51, 51, 51, 1)",
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
"fontSize": 16,
|
||||
"fontWeight": "600",
|
||||
"includeFontPadding": false,
|
||||
|
||||
+651
-40
@@ -1,81 +1,420 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`LogBoxInspectorReactFrames should render componentStack frames 1`] = `
|
||||
exports[`LogBoxInspectorReactFrames should render componentStack frames with full path pressable 1`] = `
|
||||
<LogBoxInspectorSection
|
||||
heading="Component Stack"
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"paddingHorizontal": 25,
|
||||
"paddingVertical": 4,
|
||||
"flexDirection": "row",
|
||||
"paddingHorizontal": 15,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
<LogBoxButton
|
||||
backgroundColor={
|
||||
Object {
|
||||
"default": "transparent",
|
||||
"pressed": "rgba(51, 51, 51, 1)",
|
||||
}
|
||||
}
|
||||
onPress={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingRight": 10,
|
||||
"borderRadius": 5,
|
||||
"flex": 1,
|
||||
"paddingHorizontal": 10,
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
"flexDirection": "row",
|
||||
"paddingRight": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "500",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
<
|
||||
</Text>
|
||||
MyComponent
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "500",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "500",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
}
|
||||
>
|
||||
/>
|
||||
>
|
||||
<
|
||||
</Text>
|
||||
MyComponent
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "500",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
/>
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.7)",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "300",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 16,
|
||||
"paddingLeft": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
MyComponentFile.js
|
||||
:1
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
</LogBoxButton>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"marginLeft": 15,
|
||||
}
|
||||
}
|
||||
>
|
||||
<LogBoxButton
|
||||
backgroundColor={
|
||||
Object {
|
||||
"default": "transparent",
|
||||
"pressed": "rgba(51, 51, 51, 1)",
|
||||
}
|
||||
}
|
||||
onPress={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.7)",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "300",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 16,
|
||||
"paddingLeft": 10,
|
||||
"borderRadius": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
MyComponentFile.js:1
|
||||
</Text>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.7)",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "300",
|
||||
"lineHeight": 20,
|
||||
"marginTop": 0,
|
||||
"paddingHorizontal": 10,
|
||||
"paddingVertical": 5,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</LogBoxButton>
|
||||
</View>
|
||||
</LogBoxInspectorSection>
|
||||
`;
|
||||
|
||||
exports[`LogBoxInspectorReactFrames should render componentStack frames with more than 3 stacks 1`] = `
|
||||
<LogBoxInspectorSection
|
||||
heading="Component Stack"
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingHorizontal": 15,
|
||||
}
|
||||
}
|
||||
>
|
||||
<LogBoxButton
|
||||
backgroundColor={
|
||||
Object {
|
||||
"default": "transparent",
|
||||
"pressed": "rgba(51, 51, 51, 1)",
|
||||
}
|
||||
}
|
||||
onPress={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"borderRadius": 5,
|
||||
"flex": 1,
|
||||
"paddingHorizontal": 10,
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingRight": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "500",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
<
|
||||
</Text>
|
||||
MyComponent
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "500",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
/>
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.7)",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "300",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 16,
|
||||
"paddingLeft": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
to/index.js
|
||||
:1
|
||||
</Text>
|
||||
</LogBoxButton>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingHorizontal": 15,
|
||||
}
|
||||
}
|
||||
>
|
||||
<LogBoxButton
|
||||
backgroundColor={
|
||||
Object {
|
||||
"default": "transparent",
|
||||
"pressed": "rgba(51, 51, 51, 1)",
|
||||
}
|
||||
}
|
||||
onPress={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"borderRadius": 5,
|
||||
"flex": 1,
|
||||
"paddingHorizontal": 10,
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingRight": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "500",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
<
|
||||
</Text>
|
||||
MyComponent2
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "500",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
/>
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.7)",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "300",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 16,
|
||||
"paddingLeft": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
index2.js
|
||||
:1
|
||||
</Text>
|
||||
</LogBoxButton>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingHorizontal": 15,
|
||||
}
|
||||
}
|
||||
>
|
||||
<LogBoxButton
|
||||
backgroundColor={
|
||||
Object {
|
||||
"default": "transparent",
|
||||
"pressed": "rgba(51, 51, 51, 1)",
|
||||
}
|
||||
}
|
||||
onPress={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"borderRadius": 5,
|
||||
"flex": 1,
|
||||
"paddingHorizontal": 10,
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingRight": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "500",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
<
|
||||
</Text>
|
||||
MyComponent3
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "500",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
/>
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.7)",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "300",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 16,
|
||||
"paddingLeft": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
index3.js
|
||||
:1
|
||||
</Text>
|
||||
</LogBoxButton>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
@@ -112,11 +451,283 @@ exports[`LogBoxInspectorReactFrames should render componentStack frames 1`] = `
|
||||
}
|
||||
}
|
||||
>
|
||||
See -2 more components
|
||||
See 1 more components
|
||||
</Text>
|
||||
</LogBoxButton>
|
||||
</View>
|
||||
</LogBoxInspectorSection>
|
||||
`;
|
||||
|
||||
exports[`LogBoxInspectorReactFrames should render componentStack frames with parent folder of index.js 1`] = `
|
||||
<LogBoxInspectorSection
|
||||
heading="Component Stack"
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingHorizontal": 15,
|
||||
}
|
||||
}
|
||||
>
|
||||
<LogBoxButton
|
||||
backgroundColor={
|
||||
Object {
|
||||
"default": "transparent",
|
||||
"pressed": "rgba(51, 51, 51, 1)",
|
||||
}
|
||||
}
|
||||
onPress={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"borderRadius": 5,
|
||||
"flex": 1,
|
||||
"paddingHorizontal": 10,
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingRight": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "500",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
<
|
||||
</Text>
|
||||
MyComponent
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "500",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
/>
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.7)",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "300",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 16,
|
||||
"paddingLeft": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
to/index.js
|
||||
:1
|
||||
</Text>
|
||||
</LogBoxButton>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"marginLeft": 15,
|
||||
}
|
||||
}
|
||||
>
|
||||
<LogBoxButton
|
||||
backgroundColor={
|
||||
Object {
|
||||
"default": "transparent",
|
||||
"pressed": "rgba(51, 51, 51, 1)",
|
||||
}
|
||||
}
|
||||
onPress={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"borderRadius": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.7)",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "300",
|
||||
"lineHeight": 20,
|
||||
"marginTop": 0,
|
||||
"paddingHorizontal": 10,
|
||||
"paddingVertical": 5,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</LogBoxButton>
|
||||
</View>
|
||||
</LogBoxInspectorSection>
|
||||
`;
|
||||
|
||||
exports[`LogBoxInspectorReactFrames should render componentStack frames without full path pressable 1`] = `
|
||||
<LogBoxInspectorSection
|
||||
heading="Component Stack"
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingHorizontal": 15,
|
||||
}
|
||||
}
|
||||
>
|
||||
<LogBoxButton
|
||||
backgroundColor={
|
||||
Object {
|
||||
"default": "transparent",
|
||||
"pressed": "rgba(51, 51, 51, 1)",
|
||||
}
|
||||
}
|
||||
onPress={null}
|
||||
style={
|
||||
Object {
|
||||
"borderRadius": 5,
|
||||
"flex": 1,
|
||||
"paddingHorizontal": 10,
|
||||
"paddingVertical": 4,
|
||||
}
|
||||
}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"paddingRight": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 1)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "500",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
<
|
||||
</Text>
|
||||
MyComponent
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"fontFamily": "Menlo",
|
||||
"fontSize": 14,
|
||||
"fontWeight": "500",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 18,
|
||||
}
|
||||
}
|
||||
>
|
||||
/>
|
||||
</Text>
|
||||
</Text>
|
||||
</View>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.7)",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "300",
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 16,
|
||||
"paddingLeft": 10,
|
||||
}
|
||||
}
|
||||
>
|
||||
MyComponentFile.js
|
||||
:1
|
||||
</Text>
|
||||
</LogBoxButton>
|
||||
</View>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"flexDirection": "row",
|
||||
"marginLeft": 15,
|
||||
}
|
||||
}
|
||||
>
|
||||
<LogBoxButton
|
||||
backgroundColor={
|
||||
Object {
|
||||
"default": "transparent",
|
||||
"pressed": "rgba(51, 51, 51, 1)",
|
||||
}
|
||||
}
|
||||
onPress={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"borderRadius": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Text
|
||||
style={
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.7)",
|
||||
"fontSize": 12,
|
||||
"fontWeight": "300",
|
||||
"lineHeight": 20,
|
||||
"marginTop": 0,
|
||||
"paddingHorizontal": 10,
|
||||
"paddingVertical": 5,
|
||||
}
|
||||
}
|
||||
/>
|
||||
</LogBoxButton>
|
||||
</View>
|
||||
</LogBoxInspectorSection>
|
||||
`;
|
||||
|
||||
exports[`LogBoxInspectorReactFrames should render null for no componentStack frames 1`] = `null`;
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`LogBoxLogNotification should render log 1`] = `
|
||||
exports[`LogBoxNotification should render log 1`] = `
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
@@ -0,0 +1,212 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @emails oncall+react_native
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const {
|
||||
_LogBoxNotificationContainer: LogBoxNotificationContainer,
|
||||
} = require('../LogBoxNotificationContainer');
|
||||
const LogBoxLog = require('../Data/LogBoxLog').default;
|
||||
const render = require('../../../jest/renderer');
|
||||
|
||||
describe('LogBoxNotificationContainer', () => {
|
||||
it('should render null with no logs', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxNotificationContainer selectedLogIndex={-1} logs={[]} />,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render null with no selected log and disabled', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxNotificationContainer
|
||||
isDisabled
|
||||
selectedLogIndex={-1}
|
||||
logs={[
|
||||
new LogBoxLog({
|
||||
level: 'warn',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message',
|
||||
componentStack: [],
|
||||
}),
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render the latest warning notification', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxNotificationContainer
|
||||
selectedLogIndex={-1}
|
||||
logs={[
|
||||
new LogBoxLog({
|
||||
level: 'warn',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message',
|
||||
componentStack: [],
|
||||
}),
|
||||
new LogBoxLog({
|
||||
level: 'warn',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message (latest)',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message (latest)',
|
||||
componentStack: [],
|
||||
}),
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render the latest error notification', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxNotificationContainer
|
||||
selectedLogIndex={-1}
|
||||
logs={[
|
||||
new LogBoxLog({
|
||||
level: 'error',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message',
|
||||
componentStack: [],
|
||||
}),
|
||||
new LogBoxLog({
|
||||
level: 'error',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message (latest)',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message (latest)',
|
||||
componentStack: [],
|
||||
}),
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render both an error and warning notification', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxNotificationContainer
|
||||
selectedLogIndex={-1}
|
||||
logs={[
|
||||
new LogBoxLog({
|
||||
level: 'warn',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message',
|
||||
componentStack: [],
|
||||
}),
|
||||
new LogBoxLog({
|
||||
level: 'error',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message (latest)',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message (latest)',
|
||||
componentStack: [],
|
||||
}),
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render selected fatal error even when disabled', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxNotificationContainer
|
||||
isDisabled
|
||||
selectedLogIndex={0}
|
||||
logs={[
|
||||
new LogBoxLog({
|
||||
level: 'fatal',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Should be selected',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message',
|
||||
componentStack: [],
|
||||
}),
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render selected syntax error even when disabled', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxNotificationContainer
|
||||
isDisabled
|
||||
selectedLogIndex={0}
|
||||
logs={[
|
||||
new LogBoxLog({
|
||||
level: 'syntax',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Should be selected',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of syntax error message',
|
||||
componentStack: [],
|
||||
codeFrame: {
|
||||
fileName: '/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js',
|
||||
location: {row: 199, column: 0},
|
||||
content: ` 197 | });
|
||||
198 |
|
||||
> 199 | export default CrashReactApp;
|
||||
| ^
|
||||
200 |`,
|
||||
},
|
||||
}),
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @emails oncall+react_native
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const {
|
||||
_LogBoxInspectorContainer: LogBoxInspectorContainer,
|
||||
} = require('../LogBoxInspectorContainer');
|
||||
const LogBoxLog = require('../Data/LogBoxLog').default;
|
||||
const render = require('../../../jest/renderer');
|
||||
|
||||
describe('LogBoxNotificationContainer', () => {
|
||||
it('should render inspector with logs, even when disabled', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxInspectorContainer
|
||||
isDisabled
|
||||
selectedLogIndex={-1}
|
||||
logs={[
|
||||
new LogBoxLog({
|
||||
level: 'warn',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message',
|
||||
componentStack: [],
|
||||
}),
|
||||
new LogBoxLog({
|
||||
level: 'error',
|
||||
isComponentError: false,
|
||||
message: {
|
||||
content: 'Some kind of message (latest)',
|
||||
substitutions: [],
|
||||
},
|
||||
stack: [],
|
||||
category: 'Some kind of message (latest)',
|
||||
componentStack: [],
|
||||
}),
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
+7
-100
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`LogBoxContainer should render both an error and warning notification 1`] = `
|
||||
exports[`LogBoxNotificationContainer should render both an error and warning notification 1`] = `
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
@@ -86,108 +86,15 @@ exports[`LogBoxContainer should render both an error and warning notification 1`
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`LogBoxContainer should render null with no logs 1`] = `null`;
|
||||
exports[`LogBoxNotificationContainer should render null with no logs 1`] = `null`;
|
||||
|
||||
exports[`LogBoxContainer should render null with no selected log and disabled 1`] = `null`;
|
||||
exports[`LogBoxNotificationContainer should render null with no selected log and disabled 1`] = `null`;
|
||||
|
||||
exports[`LogBoxContainer should render selected fatal error even when disabled 1`] = `
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<LogBoxInspector
|
||||
logs={
|
||||
Array [
|
||||
LogBoxLog {
|
||||
"category": "Some kind of message",
|
||||
"codeFrame": undefined,
|
||||
"componentStack": Array [],
|
||||
"count": 1,
|
||||
"isComponentError": false,
|
||||
"level": "fatal",
|
||||
"message": Object {
|
||||
"content": "Should be selected",
|
||||
"substitutions": Array [],
|
||||
},
|
||||
"stack": Array [],
|
||||
"symbolicated": Object {
|
||||
"error": null,
|
||||
"stack": null,
|
||||
"status": "NONE",
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
onChangeSelectedIndex={[Function]}
|
||||
onDismiss={[Function]}
|
||||
onMinimize={[Function]}
|
||||
selectedIndex={0}
|
||||
/>
|
||||
</View>
|
||||
`;
|
||||
exports[`LogBoxNotificationContainer should render selected fatal error even when disabled 1`] = `null`;
|
||||
|
||||
exports[`LogBoxContainer should render selected syntax error even when disabled 1`] = `
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<LogBoxInspector
|
||||
logs={
|
||||
Array [
|
||||
LogBoxLog {
|
||||
"category": "Some kind of syntax error message",
|
||||
"codeFrame": Object {
|
||||
"content": " 197 | });
|
||||
198 |
|
||||
> 199 | export default CrashReactApp;
|
||||
| ^
|
||||
200 |",
|
||||
"fileName": "/path/to/RKJSModules/Apps/CrashReact/CrashReactApp.js",
|
||||
"location": Object {
|
||||
"column": 0,
|
||||
"row": 199,
|
||||
},
|
||||
},
|
||||
"componentStack": Array [],
|
||||
"count": 1,
|
||||
"isComponentError": false,
|
||||
"level": "syntax",
|
||||
"message": Object {
|
||||
"content": "Should be selected",
|
||||
"substitutions": Array [],
|
||||
},
|
||||
"stack": Array [],
|
||||
"symbolicated": Object {
|
||||
"error": null,
|
||||
"stack": null,
|
||||
"status": "NONE",
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
onChangeSelectedIndex={[Function]}
|
||||
onDismiss={[Function]}
|
||||
onMinimize={[Function]}
|
||||
selectedIndex={0}
|
||||
/>
|
||||
</View>
|
||||
`;
|
||||
exports[`LogBoxNotificationContainer should render selected syntax error even when disabled 1`] = `null`;
|
||||
|
||||
exports[`LogBoxContainer should render the latest error notification 1`] = `
|
||||
exports[`LogBoxNotificationContainer should render the latest error notification 1`] = `
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
@@ -237,7 +144,7 @@ exports[`LogBoxContainer should render the latest error notification 1`] = `
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`LogBoxContainer should render the latest warning notification 1`] = `
|
||||
exports[`LogBoxNotificationContainer should render the latest warning notification 1`] = `
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
@@ -0,0 +1,66 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`LogBoxNotificationContainer should render inspector with logs, even when disabled 1`] = `
|
||||
<NativeLogBoxVisibility
|
||||
visible={false}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"bottom": 0,
|
||||
"left": 0,
|
||||
"position": "absolute",
|
||||
"right": 0,
|
||||
"top": 0,
|
||||
}
|
||||
}
|
||||
>
|
||||
<LogBoxInspector
|
||||
logs={
|
||||
Array [
|
||||
LogBoxLog {
|
||||
"category": "Some kind of message",
|
||||
"codeFrame": undefined,
|
||||
"componentStack": Array [],
|
||||
"count": 1,
|
||||
"isComponentError": false,
|
||||
"level": "warn",
|
||||
"message": Object {
|
||||
"content": "Some kind of message",
|
||||
"substitutions": Array [],
|
||||
},
|
||||
"stack": Array [],
|
||||
"symbolicated": Object {
|
||||
"error": null,
|
||||
"stack": null,
|
||||
"status": "NONE",
|
||||
},
|
||||
},
|
||||
LogBoxLog {
|
||||
"category": "Some kind of message (latest)",
|
||||
"codeFrame": undefined,
|
||||
"componentStack": Array [],
|
||||
"count": 1,
|
||||
"isComponentError": false,
|
||||
"level": "error",
|
||||
"message": Object {
|
||||
"content": "Some kind of message (latest)",
|
||||
"substitutions": Array [],
|
||||
},
|
||||
"stack": Array [],
|
||||
"symbolicated": Object {
|
||||
"error": null,
|
||||
"stack": null,
|
||||
"status": "NONE",
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
onChangeSelectedIndex={[Function]}
|
||||
onDismiss={[Function]}
|
||||
onMinimize={[Function]}
|
||||
selectedIndex={-1}
|
||||
/>
|
||||
</View>
|
||||
</NativeLogBoxVisibility>
|
||||
`;
|
||||
@@ -229,7 +229,7 @@ class Modal extends React.Component<Props> {
|
||||
}
|
||||
|
||||
const innerChildren = __DEV__ ? (
|
||||
<AppContainer rootTag={this.context.rootTag} internal_excludeLogBox>
|
||||
<AppContainer rootTag={this.context.rootTag}>
|
||||
{this.props.children}
|
||||
</AppContainer>
|
||||
) : (
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import type {TurboModule} from 'react-native/Libraries/TurboModule/RCTExport';
|
||||
import * as TurboModuleRegistry from 'react-native/Libraries/TurboModule/TurboModuleRegistry';
|
||||
|
||||
export interface Spec extends TurboModule {
|
||||
+show: () => void;
|
||||
+hide: () => void;
|
||||
}
|
||||
|
||||
export default (TurboModuleRegistry.get<Spec>('LogBox'): ?Spec);
|
||||
@@ -94,16 +94,14 @@ class AppContainer extends React.Component<Props, State> {
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
let logBox = null;
|
||||
if (__DEV__ && !this.props.internal_excludeLogBox) {
|
||||
if (!global.__RCTProfileIsProfiling) {
|
||||
if (global.__reactExperimentalLogBox) {
|
||||
const LogBox = require('../LogBox/LogBox');
|
||||
logBox = <LogBox />;
|
||||
} else {
|
||||
const YellowBox = require('../YellowBox/YellowBox');
|
||||
logBox = <YellowBox />;
|
||||
}
|
||||
let yellowBox = null;
|
||||
if (__DEV__) {
|
||||
if (
|
||||
!global.__RCTProfileIsProfiling &&
|
||||
!this.props.internal_excludeLogBox
|
||||
) {
|
||||
const YellowBox = require('../YellowBox/YellowBox');
|
||||
yellowBox = <YellowBox />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +135,7 @@ class AppContainer extends React.Component<Props, State> {
|
||||
<View style={styles.appContainer} pointerEvents="box-none">
|
||||
{!this.state.hasError && innerView}
|
||||
{this.state.inspector}
|
||||
{logBox}
|
||||
{yellowBox}
|
||||
</View>
|
||||
</RootTagContext.Provider>
|
||||
);
|
||||
@@ -152,22 +150,8 @@ const styles = StyleSheet.create({
|
||||
|
||||
if (__DEV__) {
|
||||
if (!global.__RCTProfileIsProfiling) {
|
||||
if (global.__reactExperimentalLogBox) {
|
||||
const LogBox = require('../LogBox/LogBox');
|
||||
LogBox.install();
|
||||
|
||||
// TODO: (rickhanlonii) T57484314 Temporary hack to fix LogBox experiment but we need to
|
||||
// either decide to provide an error boundary by default or move this to a separate root.
|
||||
AppContainer.getDerivedStateFromError = function getDerivedStateFromError(
|
||||
error,
|
||||
state,
|
||||
) {
|
||||
return {...state, hasError: true};
|
||||
};
|
||||
} else {
|
||||
const YellowBox = require('../YellowBox/YellowBox');
|
||||
YellowBox.install();
|
||||
}
|
||||
const YellowBox = require('../YellowBox/YellowBox');
|
||||
YellowBox.install();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,6 +125,7 @@ const AppRegistry = {
|
||||
appParameters.fabric,
|
||||
showArchitectureIndicator,
|
||||
scopedPerformanceLogger,
|
||||
appKey === 'LogBox',
|
||||
);
|
||||
},
|
||||
};
|
||||
@@ -180,13 +181,15 @@ const AppRegistry = {
|
||||
* See http://facebook.github.io/react-native/docs/appregistry.html#runapplication
|
||||
*/
|
||||
runApplication(appKey: string, appParameters: any): void {
|
||||
const msg =
|
||||
'Running "' + appKey + '" with ' + JSON.stringify(appParameters);
|
||||
infoLog(msg);
|
||||
BugReporting.addSource(
|
||||
'AppRegistry.runApplication' + runCount++,
|
||||
() => msg,
|
||||
);
|
||||
if (appKey !== 'LogBox') {
|
||||
const msg =
|
||||
'Running "' + appKey + '" with ' + JSON.stringify(appParameters);
|
||||
infoLog(msg);
|
||||
BugReporting.addSource(
|
||||
'AppRegistry.runApplication' + runCount++,
|
||||
() => msg,
|
||||
);
|
||||
}
|
||||
invariant(
|
||||
runnables[appKey] && runnables[appKey].run,
|
||||
`"${appKey}" has not been registered. This can happen if:\n` +
|
||||
@@ -292,4 +295,9 @@ const AppRegistry = {
|
||||
|
||||
BatchedBridge.registerCallableModule('AppRegistry', AppRegistry);
|
||||
|
||||
if (__DEV__) {
|
||||
const LogBoxInspector = require('../LogBox/LogBoxInspectorContainer').default;
|
||||
AppRegistry.registerComponent('LogBox', () => LogBoxInspector);
|
||||
}
|
||||
|
||||
module.exports = AppRegistry;
|
||||
|
||||
@@ -29,6 +29,7 @@ function renderApplication<Props: Object>(
|
||||
fabric?: boolean,
|
||||
showArchitectureIndicator?: boolean,
|
||||
scopedPerformanceLogger?: IPerformanceLogger,
|
||||
isLogBox?: boolean,
|
||||
) {
|
||||
invariant(rootTag, 'Expect to have a valid rootTag, instead got ', rootTag);
|
||||
|
||||
@@ -39,7 +40,8 @@ function renderApplication<Props: Object>(
|
||||
rootTag={rootTag}
|
||||
fabric={fabric}
|
||||
showArchitectureIndicator={showArchitectureIndicator}
|
||||
WrapperComponent={WrapperComponent}>
|
||||
WrapperComponent={WrapperComponent}
|
||||
internal_excludeLogBox={isLogBox}>
|
||||
<RootComponent {...initialProps} rootTag={rootTag} />
|
||||
</AppContainer>
|
||||
</PerformanceLoggerContext.Provider>
|
||||
|
||||
@@ -12,12 +12,7 @@
|
||||
|
||||
const React = require('react');
|
||||
|
||||
import type {Category} from './Data/YellowBoxCategory';
|
||||
import type {
|
||||
Registry,
|
||||
Subscription,
|
||||
IgnorePattern,
|
||||
} from './Data/YellowBoxRegistry';
|
||||
import type {Registry, IgnorePattern} from './Data/YellowBoxRegistry';
|
||||
import * as LogBoxData from '../LogBox/Data/LogBoxData';
|
||||
type Props = $ReadOnly<{||}>;
|
||||
type State = {|
|
||||
@@ -47,14 +42,19 @@ let YellowBox;
|
||||
if (__DEV__) {
|
||||
const Platform = require('../Utilities/Platform');
|
||||
const RCTLog = require('../Utilities/RCTLog');
|
||||
const YellowBoxList = require('./UI/YellowBoxList');
|
||||
const YellowBoxContainer = require('./YellowBoxContainer').default;
|
||||
const LogBox = require('../LogBox/LogBox');
|
||||
const YellowBoxRegistry = require('./Data/YellowBoxRegistry');
|
||||
const LogBoxNotificationContainer = require('../LogBox/LogBoxNotificationContainer')
|
||||
.default;
|
||||
|
||||
// YellowBox needs to insert itself early,
|
||||
// in order to access the component stacks appended by React DevTools.
|
||||
const {error, warn} = console;
|
||||
let errorImpl = error;
|
||||
let warnImpl = warn;
|
||||
let _isLogBoxEnabled = false;
|
||||
let _isInstalled = false;
|
||||
(console: any).error = function(...args) {
|
||||
errorImpl(...args);
|
||||
};
|
||||
@@ -70,6 +70,11 @@ if (__DEV__) {
|
||||
}
|
||||
|
||||
static install(): void {
|
||||
_isInstalled = true;
|
||||
if (_isLogBoxEnabled) {
|
||||
LogBox.install();
|
||||
return;
|
||||
}
|
||||
errorImpl = function(...args) {
|
||||
error.call(console, ...args);
|
||||
// Show YellowBox for the `warning` module.
|
||||
@@ -102,46 +107,39 @@ if (__DEV__) {
|
||||
}
|
||||
|
||||
static uninstall(): void {
|
||||
if (_isLogBoxEnabled) {
|
||||
LogBox.uninstall();
|
||||
return;
|
||||
}
|
||||
_isInstalled = false;
|
||||
errorImpl = error;
|
||||
warnImpl = warn;
|
||||
delete (console: any).disableYellowBox;
|
||||
}
|
||||
|
||||
_subscription: ?Subscription;
|
||||
static __unstable_enableLogBox(): void {
|
||||
if (_isInstalled) {
|
||||
throw new Error(
|
||||
'LogBox must be enabled before AppContainer is required so that it can properly wrap the console methods.\n\nPlease enable LogBox earlier in your app.\n\n',
|
||||
);
|
||||
}
|
||||
_isLogBoxEnabled = true;
|
||||
|
||||
state = {
|
||||
registry: null,
|
||||
};
|
||||
// TODO: Temporary hack to prevent cycles with the ExceptionManager.
|
||||
global.__unstable_isLogBoxEnabled = true;
|
||||
}
|
||||
|
||||
static __unstable_isLogBoxEnabled(): boolean {
|
||||
return !!_isLogBoxEnabled;
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
// TODO: Ignore warnings that fire when rendering `YellowBox` itself.
|
||||
return this.state.registry == null ? null : (
|
||||
<YellowBoxList
|
||||
onDismiss={this._handleDismiss}
|
||||
onDismissAll={this._handleDismissAll}
|
||||
registry={this.state.registry}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this._subscription = YellowBoxRegistry.observe(registry => {
|
||||
this.setState({registry});
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (this._subscription != null) {
|
||||
this._subscription.unsubscribe();
|
||||
if (_isLogBoxEnabled) {
|
||||
return <LogBoxNotificationContainer />;
|
||||
}
|
||||
}
|
||||
|
||||
_handleDismiss = (category: Category): void => {
|
||||
YellowBoxRegistry.delete(category);
|
||||
};
|
||||
|
||||
_handleDismissAll(): void {
|
||||
YellowBoxRegistry.clear();
|
||||
// TODO: Ignore warnings that fire when rendering `YellowBox` itself.
|
||||
return <YellowBoxContainer />;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -162,6 +160,13 @@ if (__DEV__) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
static __unstable_enableLogBox(): void {
|
||||
// Do nothing.
|
||||
}
|
||||
static __unstable_isLogBoxEnabled(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
render(): React.Node {
|
||||
return null;
|
||||
}
|
||||
@@ -172,5 +177,7 @@ module.exports = (YellowBox: Class<React.Component<Props, State>> & {
|
||||
ignoreWarnings($ReadOnlyArray<IgnorePattern>): void,
|
||||
install(): void,
|
||||
uninstall(): void,
|
||||
__unstable_enableLogBox(): void,
|
||||
__unstable_isLogBoxEnabled(): boolean,
|
||||
...
|
||||
});
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @format
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
|
||||
import type {Category} from './Data/YellowBoxCategory';
|
||||
import type {Registry, Subscription} from './Data/YellowBoxRegistry';
|
||||
|
||||
type Props = $ReadOnly<{||}>;
|
||||
type State = $ReadOnly<{|
|
||||
registry: ?Registry,
|
||||
|}>;
|
||||
|
||||
const YellowBoxList = require('./UI/YellowBoxList');
|
||||
const YellowBoxRegistry = require('./Data/YellowBoxRegistry');
|
||||
|
||||
class YellowBoxContainer extends React.Component<Props, State> {
|
||||
_subscription: ?Subscription;
|
||||
|
||||
state: State = {
|
||||
registry: null,
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
// TODO: Ignore warnings that fire when rendering `YellowBox` itself.
|
||||
return this.state.registry == null ? null : (
|
||||
<YellowBoxList
|
||||
onDismiss={this._handleDismiss}
|
||||
onDismissAll={this._handleDismissAll}
|
||||
registry={this.state.registry}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this._subscription = YellowBoxRegistry.observe(registry => {
|
||||
this.setState({registry});
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (this._subscription != null) {
|
||||
this._subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
_handleDismiss = (category: Category): void => {
|
||||
YellowBoxRegistry.delete(category);
|
||||
};
|
||||
|
||||
_handleDismissAll(): void {
|
||||
YellowBoxRegistry.clear();
|
||||
}
|
||||
}
|
||||
|
||||
export default YellowBoxContainer;
|
||||
@@ -11,8 +11,16 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as React from 'react';
|
||||
const YellowBox = require('../YellowBox');
|
||||
const YellowBoxRegistry = require('../Data/YellowBoxRegistry');
|
||||
const LogBoxData = require('../../LogBox/Data/LogBoxData');
|
||||
const render = require('../../../jest/renderer');
|
||||
|
||||
jest.mock('../../LogBox/LogBoxNotificationContainer', () => ({
|
||||
__esModule: true,
|
||||
default: 'LogBoxNotificationContainer',
|
||||
}));
|
||||
|
||||
describe('YellowBox', () => {
|
||||
const {error, warn} = console;
|
||||
@@ -74,4 +82,49 @@ describe('YellowBox', () => {
|
||||
(console: any).error('Warning: ...');
|
||||
expect(YellowBoxRegistry.add).toBeCalled();
|
||||
});
|
||||
|
||||
it('if LogBox is enabled, installs and uninstalls LogBox', () => {
|
||||
jest.mock('../../LogBox/Data/LogBoxData');
|
||||
jest.mock('../Data/YellowBoxRegistry');
|
||||
|
||||
YellowBox.__unstable_enableLogBox();
|
||||
YellowBox.install();
|
||||
|
||||
(console: any).warn('Some warning');
|
||||
expect(YellowBoxRegistry.add).not.toBeCalled();
|
||||
expect(LogBoxData.addLog).toBeCalled();
|
||||
expect(YellowBox.__unstable_isLogBoxEnabled()).toBe(true);
|
||||
|
||||
YellowBox.uninstall();
|
||||
(LogBoxData.addLog: any).mockClear();
|
||||
|
||||
(console: any).warn('Some warning');
|
||||
expect(YellowBoxRegistry.add).not.toBeCalled();
|
||||
expect(LogBoxData.addLog).not.toBeCalled();
|
||||
expect(YellowBox.__unstable_isLogBoxEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('throws if LogBox is enabled after YellowBox is installed', () => {
|
||||
jest.mock('../Data/YellowBoxRegistry');
|
||||
|
||||
YellowBox.install();
|
||||
|
||||
expect(() => YellowBox.__unstable_enableLogBox()).toThrow(
|
||||
'LogBox must be enabled before AppContainer is required so that it can properly wrap the console methods.\n\nPlease enable LogBox earlier in your app.\n\n',
|
||||
);
|
||||
});
|
||||
|
||||
it('should render YellowBoxContainer by default', () => {
|
||||
const output = render.shallowRender(<YellowBox />);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render LogBoxNotificationContainer when LogBox is enabled', () => {
|
||||
YellowBox.__unstable_enableLogBox();
|
||||
|
||||
const output = render.shallowRender(<YellowBox />);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`YellowBox should render LogBoxNotificationContainer when LogBox is enabled 1`] = `<LogBoxNotificationContainer />`;
|
||||
|
||||
exports[`YellowBox should render YellowBoxContainer by default 1`] = `<YellowBoxContainer />`;
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <React/RCTDefines.h>
|
||||
|
||||
RCT_EXTERN NSString *const RCTUserInterfaceStyleDidChangeNotification;
|
||||
RCT_EXTERN NSString *const RCTUserInterfaceStyleDidChangeNotificationTraitCollectionKey;
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RCTConstants.h"
|
||||
|
||||
NSString *const RCTUserInterfaceStyleDidChangeNotification = @"RCTUserInterfaceStyleDidChangeNotification";
|
||||
NSString *const RCTUserInterfaceStyleDidChangeNotificationTraitCollectionKey = @"traitCollection";
|
||||
@@ -14,6 +14,7 @@
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTBridge+Private.h"
|
||||
#import "RCTConstants.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTKeyCommands.h"
|
||||
#import "RCTLog.h"
|
||||
@@ -33,7 +34,6 @@
|
||||
#endif
|
||||
|
||||
NSString *const RCTContentDidAppearNotification = @"RCTContentDidAppearNotification";
|
||||
static NSString *const RCTUserInterfaceStyleDidChangeNotification = @"RCTUserInterfaceStyleDidChangeNotification";
|
||||
|
||||
@interface RCTUIManager (RCTRootView)
|
||||
|
||||
@@ -367,21 +367,16 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||
[self showLoadingView];
|
||||
}
|
||||
|
||||
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
|
||||
__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
|
||||
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
|
||||
{
|
||||
[super traitCollectionDidChange:previousTraitCollection];
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
if ([previousTraitCollection hasDifferentColorAppearanceComparedToTraitCollection:self.traitCollection]) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTUserInterfaceStyleDidChangeNotification
|
||||
object:self
|
||||
userInfo:@{@"traitCollection": self.traitCollection}];
|
||||
}
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTUserInterfaceStyleDidChangeNotification
|
||||
object:self
|
||||
userInfo:@{
|
||||
RCTUserInterfaceStyleDidChangeNotificationTraitCollectionKey: self.traitCollection,
|
||||
}];
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
|
||||
@@ -21,11 +21,11 @@ NSDictionary* RCTGetReactNativeVersion(void)
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^(void){
|
||||
__rnVersion = @{
|
||||
RCTVersionMajor: @(0),
|
||||
RCTVersionMinor: @(0),
|
||||
RCTVersionPatch: @(0),
|
||||
RCTVersionPrerelease: [NSNull null],
|
||||
};
|
||||
RCTVersionMajor: @(0),
|
||||
RCTVersionMinor: @(62),
|
||||
RCTVersionPatch: @(0),
|
||||
RCTVersionPrerelease: @"rc.0",
|
||||
};
|
||||
});
|
||||
return __rnVersion;
|
||||
}
|
||||
|
||||
@@ -7,14 +7,13 @@
|
||||
|
||||
#import "RCTSurfaceHostingView.h"
|
||||
|
||||
#import "RCTConstants.h"
|
||||
#import "RCTDefines.h"
|
||||
#import "RCTSurface.h"
|
||||
#import "RCTSurfaceDelegate.h"
|
||||
#import "RCTSurfaceView.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
static NSString *const RCTUserInterfaceStyleDidChangeNotification = @"RCTUserInterfaceStyleDidChangeNotification";
|
||||
|
||||
@interface RCTSurfaceHostingView ()
|
||||
|
||||
@property (nonatomic, assign) BOOL isActivityIndicatorViewVisible;
|
||||
@@ -208,21 +207,15 @@ RCT_NOT_IMPLEMENTED(- (nullable instancetype)initWithCoder:(NSCoder *)coder)
|
||||
|
||||
#pragma mark - UITraitCollection updates
|
||||
|
||||
#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && defined(__IPHONE_13_0) && \
|
||||
__IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_13_0
|
||||
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
|
||||
{
|
||||
[super traitCollectionDidChange:previousTraitCollection];
|
||||
|
||||
if (@available(iOS 13.0, *)) {
|
||||
if ([previousTraitCollection hasDifferentColorAppearanceComparedToTraitCollection:self.traitCollection]) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTUserInterfaceStyleDidChangeNotification
|
||||
object:self
|
||||
userInfo:@{@"traitCollection": self.traitCollection}];
|
||||
}
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTUserInterfaceStyleDidChangeNotification
|
||||
object:self
|
||||
userInfo:@{
|
||||
RCTUserInterfaceStyleDidChangeNotificationTraitCollectionKey: self.traitCollection,
|
||||
}];
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark - Private stuff
|
||||
|
||||
|
||||
@@ -96,6 +96,9 @@ rn_apple_library(
|
||||
) + react_module_plugin_providers(
|
||||
name = "RedBox",
|
||||
native_class_func = "RCTRedBoxCls",
|
||||
) + react_module_plugin_providers(
|
||||
name = "LogBox",
|
||||
native_class_func = "RCTLogBoxCls",
|
||||
) + react_module_plugin_providers(
|
||||
name = "TVNavigationEventEmitter",
|
||||
native_class_func = "RCTTVNavigationEventEmitterCls",
|
||||
|
||||
@@ -48,6 +48,7 @@ Class RCTPerfMonitorCls(void) __attribute__((used));
|
||||
Class RCTDevMenuCls(void) __attribute__((used));
|
||||
Class RCTDevSettingsCls(void) __attribute__((used));
|
||||
Class RCTRedBoxCls(void) __attribute__((used));
|
||||
Class RCTLogBoxCls(void) __attribute__((used));
|
||||
Class RCTTVNavigationEventEmitterCls(void) __attribute__((used));
|
||||
Class RCTWebSocketExecutorCls(void) __attribute__((used));
|
||||
Class RCTWebSocketModuleCls(void) __attribute__((used));
|
||||
|
||||
@@ -37,6 +37,7 @@ Class RCTCoreModulesClassProvider(const char *name) {
|
||||
{"DevMenu", RCTDevMenuCls},
|
||||
{"DevSettings", RCTDevSettingsCls},
|
||||
{"RedBox", RCTRedBoxCls},
|
||||
{"LogBox", RCTLogBoxCls},
|
||||
{"TVNavigationEventEmitter", RCTTVNavigationEventEmitterCls},
|
||||
{"WebSocketExecutor", RCTWebSocketExecutorCls},
|
||||
{"WebSocketModule", RCTWebSocketModuleCls},
|
||||
|
||||
@@ -12,7 +12,5 @@
|
||||
|
||||
RCT_EXTERN void RCTEnableAppearancePreference(BOOL enabled);
|
||||
|
||||
NSString *const RCTUserInterfaceStyleDidChangeNotification = @"RCTUserInterfaceStyleDidChangeNotification";
|
||||
|
||||
@interface RCTAppearance : RCTEventEmitter <RCTBridgeModule>
|
||||
@end
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#import "RCTAppearance.h"
|
||||
|
||||
#import <FBReactNativeSpec/FBReactNativeSpec.h>
|
||||
#import <React/RCTConstants.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
|
||||
#import "CoreModulesPlugins.h"
|
||||
@@ -86,7 +87,7 @@ RCT_EXPORT_SYNCHRONOUS_TYPED_METHOD(NSString *, getColorScheme)
|
||||
NSDictionary *userInfo = [notification userInfo];
|
||||
UITraitCollection *traitCollection = nil;
|
||||
if (userInfo) {
|
||||
traitCollection = userInfo[@"traitCollection"];
|
||||
traitCollection = userInfo[RCTUserInterfaceStyleDidChangeNotificationTraitCollectionKey];
|
||||
}
|
||||
NSString *newColorScheme = RCTColorSchemePreference(traitCollection);
|
||||
if (![_currentColorScheme isEqualToString:newColorScheme]) {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#import <FBReactNativeSpec/FBReactNativeSpec.h>
|
||||
#import <React/RCTAccessibilityManager.h>
|
||||
#import <React/RCTAssert.h>
|
||||
#import <React/RCTConstants.h>
|
||||
#import <React/RCTEventDispatcher.h>
|
||||
#import <React/RCTUIUtils.h>
|
||||
#import <React/RCTUtils.h>
|
||||
@@ -61,9 +62,15 @@ RCT_EXPORT_MODULE()
|
||||
_currentInterfaceDimensions = RCTExportedDimensions(_bridge);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(interfaceFrameDidChange)
|
||||
name:UIApplicationDidBecomeActiveNotification
|
||||
object:nil];
|
||||
selector:@selector(interfaceFrameDidChange)
|
||||
name:UIApplicationDidBecomeActiveNotification
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(interfaceFrameDidChange)
|
||||
name:RCTUserInterfaceStyleDidChangeNotification
|
||||
object:nil];
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTErrorCustomizer.h>
|
||||
|
||||
@class RCTJSStackFrame;
|
||||
|
||||
@interface RCTLogBox : NSObject <RCTBridgeModule>
|
||||
|
||||
- (void)show;
|
||||
- (void)hide;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RCTLogBox.h"
|
||||
|
||||
#import <FBReactNativeSpec/FBReactNativeSpec.h>
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTRootView.h>
|
||||
#import <React/RCTConvert.h>
|
||||
#import <React/RCTDefines.h>
|
||||
#import <React/RCTErrorInfo.h>
|
||||
#import <React/RCTEventDispatcher.h>
|
||||
#import <React/RCTJSStackFrame.h>
|
||||
#import <React/RCTReloadCommand.h>
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import "CoreModulesPlugins.h"
|
||||
|
||||
#if RCT_DEV_MENU
|
||||
|
||||
@class RCTLogBoxWindow;
|
||||
|
||||
@protocol RCTLogBoxWindowActionDelegate <NSObject>
|
||||
|
||||
- (void)logBoxWindow:(RCTLogBoxWindow *)logBoxWindow openStackFrameInEditor:(RCTJSStackFrame *)stackFrame;
|
||||
- (void)reloadFromlogBoxWindow:(RCTLogBoxWindow *)logBoxWindow;
|
||||
- (void)loadExtraDataViewController;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTLogBoxWindow : UIWindow <UITableViewDelegate>
|
||||
@property (nonatomic, weak) id<RCTLogBoxWindowActionDelegate> actionDelegate;
|
||||
@end
|
||||
|
||||
@implementation RCTLogBoxWindow
|
||||
{
|
||||
UITableView *_stackTraceTableView;
|
||||
NSString *_lastErrorMessage;
|
||||
NSArray<RCTJSStackFrame *> *_lastStackTrace;
|
||||
int _lastErrorCookie;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame bridge:(RCTBridge *)bridge
|
||||
{
|
||||
_lastErrorCookie = -1;
|
||||
if ((self = [super initWithFrame:frame])) {
|
||||
#if TARGET_OS_TV
|
||||
self.windowLevel = UIWindowLevelAlert + 1000;
|
||||
#else
|
||||
self.windowLevel = UIWindowLevelStatusBar - 1;
|
||||
#endif
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.hidden = YES;
|
||||
|
||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"LogBox" initialProperties:nil];
|
||||
|
||||
UIViewController *rootViewController = [UIViewController new];
|
||||
rootViewController.view = rootView;
|
||||
self.rootViewController = rootViewController;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)show
|
||||
{
|
||||
[self becomeFirstResponder];
|
||||
[self makeKeyAndVisible];
|
||||
}
|
||||
|
||||
- (void)dismiss
|
||||
{
|
||||
self.hidden = YES;
|
||||
[self resignFirstResponder];
|
||||
[RCTSharedApplication().delegate.window makeKeyWindow];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTLogBox () <NativeLogBoxSpec>
|
||||
@end
|
||||
|
||||
@implementation RCTLogBox
|
||||
{
|
||||
RCTLogBoxWindow *_window;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_bridge = bridge;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self->_window = [[RCTLogBoxWindow alloc] initWithFrame:[UIScreen mainScreen].bounds bridge: self->_bridge];
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(show)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self->_window show];
|
||||
});
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(hide)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self->_window dismiss];
|
||||
});
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModuleWithJsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
|
||||
{
|
||||
return std::make_shared<facebook::react::NativeLogBoxSpecJSI>(self, jsInvoker);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else // Disabled
|
||||
|
||||
@interface RCTLogBox() <NativeLogBoxSpec>
|
||||
@end
|
||||
|
||||
@implementation RCTLogBox
|
||||
|
||||
+ (NSString *)moduleName
|
||||
{
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)show {
|
||||
// noop
|
||||
}
|
||||
|
||||
- (void)hide {
|
||||
// noop
|
||||
}
|
||||
|
||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModuleWithJsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
|
||||
{
|
||||
return std::make_shared<facebook::react::NativeLogBoxSpecJSI>(self, jsInvoker);
|
||||
}
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
Class RCTLogBoxCls(void) {
|
||||
return RCTLogBox.class;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=1000.0.0-master
|
||||
VERSION_NAME=0.62.0-rc.0
|
||||
GROUP=com.facebook.react
|
||||
|
||||
POM_NAME=ReactNative
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||
* directory of this source tree.
|
||||
*
|
||||
* <p>Generated by an internal genrule from Flow types.
|
||||
*
|
||||
* @generated
|
||||
* @nolint
|
||||
*/
|
||||
|
||||
package com.facebook.fbreact.specs;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReactModuleWithSpec;
|
||||
import com.facebook.react.turbomodule.core.interfaces.TurboModule;
|
||||
|
||||
public abstract class NativeLogBoxSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule {
|
||||
public NativeLogBoxSpec(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public abstract void hide();
|
||||
|
||||
@ReactMethod
|
||||
public abstract void show();
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.NativeModule;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactMarker;
|
||||
import com.facebook.react.devsupport.LogBoxModule;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
import com.facebook.react.module.annotations.ReactModuleList;
|
||||
import com.facebook.react.module.model.ReactModuleInfo;
|
||||
@@ -49,6 +50,7 @@ import java.util.Map;
|
||||
DeviceInfoModule.class,
|
||||
DevSettingsModule.class,
|
||||
ExceptionsManagerModule.class,
|
||||
LogBoxModule.class,
|
||||
HeadlessJsTaskSupportModule.class,
|
||||
SourceCodeModule.class,
|
||||
TimingModule.class,
|
||||
@@ -94,6 +96,7 @@ import java.util.Map;
|
||||
DeviceInfoModule.class,
|
||||
DevSettingsModule.class,
|
||||
ExceptionsManagerModule.class,
|
||||
LogBoxModule.class,
|
||||
HeadlessJsTaskSupportModule.class,
|
||||
SourceCodeModule.class,
|
||||
TimingModule.class,
|
||||
@@ -142,6 +145,8 @@ import java.util.Map;
|
||||
return new DevSettingsModule(reactContext, mReactInstanceManager.getDevSupportManager());
|
||||
case ExceptionsManagerModule.NAME:
|
||||
return new ExceptionsManagerModule(mReactInstanceManager.getDevSupportManager());
|
||||
case LogBoxModule.NAME:
|
||||
return new LogBoxModule(reactContext, mReactInstanceManager.getDevSupportManager());
|
||||
case HeadlessJsTaskSupportModule.NAME:
|
||||
return new HeadlessJsTaskSupportModule(reactContext);
|
||||
case SourceCodeModule.NAME:
|
||||
|
||||
@@ -304,6 +304,27 @@ public class ReactInstanceManager {
|
||||
public JavaScriptExecutorFactory getJavaScriptExecutorFactory() {
|
||||
return ReactInstanceManager.this.getJSExecutorFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable View createRootView(String appKey) {
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
if (currentActivity != null) {
|
||||
ReactRootView rootView = new ReactRootView(currentActivity);
|
||||
|
||||
rootView.startReactApplication(ReactInstanceManager.this, appKey, null);
|
||||
|
||||
return rootView;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyRootView(View rootView) {
|
||||
if (rootView instanceof ReactRootView) {
|
||||
((ReactRootView) rootView).unmountReactApplication();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.SensorManager;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -377,6 +378,14 @@ public class DevSupportManagerImpl
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable View createRootView(String appKey) {
|
||||
return mReactInstanceManagerHelper.createRootView(appKey);
|
||||
}
|
||||
|
||||
public void destroyRootView(View rootView) {
|
||||
mReactInstanceManagerHelper.destroyRootView(rootView);
|
||||
}
|
||||
|
||||
private void hideDevOptionsDialog() {
|
||||
if (mDevOptionsDialog != null) {
|
||||
mDevOptionsDialog.dismiss();
|
||||
|
||||
+9
@@ -7,6 +7,7 @@
|
||||
|
||||
package com.facebook.react.devsupport;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.DefaultNativeModuleCallExceptionHandler;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
@@ -40,6 +41,14 @@ public class DisabledDevSupportManager implements DevSupportManager {
|
||||
@Override
|
||||
public void showNewJSError(String message, ReadableArray details, int errorCookie) {}
|
||||
|
||||
@Override
|
||||
public @Nullable View createRootView(String appKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyRootView(View rootView) {}
|
||||
|
||||
@Override
|
||||
public void updateJSError(String message, ReadableArray details, int errorCookie) {}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.devsupport;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import com.facebook.react.R;
|
||||
|
||||
/** Dialog for displaying JS errors in LogBox. */
|
||||
public class LogBoxDialog extends Dialog {
|
||||
public LogBoxDialog(Activity context, View reactRootView) {
|
||||
super(context, R.style.Theme_Catalyst_LogBox);
|
||||
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(reactRootView);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.devsupport;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.devsupport.interfaces.DevSupportManager;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
|
||||
@ReactModule(name = LogBoxModule.NAME)
|
||||
public class LogBoxModule extends ReactContextBaseJavaModule {
|
||||
|
||||
public static final String NAME = "LogBox";
|
||||
|
||||
private final DevSupportManager mDevSupportManager;
|
||||
private @Nullable View mReactRootView;
|
||||
private @Nullable LogBoxDialog mLogBoxDialog;
|
||||
|
||||
public LogBoxModule(ReactApplicationContext reactContext, DevSupportManager devSupportManager) {
|
||||
super(reactContext);
|
||||
|
||||
mDevSupportManager = devSupportManager;
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mReactRootView == null) {
|
||||
mReactRootView = mDevSupportManager.createRootView("LogBox");
|
||||
if (mReactRootView == null) {
|
||||
FLog.e(
|
||||
ReactConstants.TAG,
|
||||
"Unable to launch logbox because react was unable to create the root view");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void show() {
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mLogBoxDialog == null) {
|
||||
Activity context = getCurrentActivity();
|
||||
if (context == null || context.isFinishing()) {
|
||||
FLog.e(
|
||||
ReactConstants.TAG,
|
||||
"Unable to launch logbox because react activity "
|
||||
+ "is not available, here is the error that logbox would've displayed: ");
|
||||
return;
|
||||
}
|
||||
mLogBoxDialog = new LogBoxDialog(context, mReactRootView);
|
||||
mLogBoxDialog.setCancelable(false);
|
||||
mLogBoxDialog.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void hide() {
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mLogBoxDialog != null) {
|
||||
if (mReactRootView.getParent() != null) {
|
||||
((ViewGroup) mReactRootView.getParent()).removeView(mReactRootView);
|
||||
}
|
||||
mLogBoxDialog.dismiss();
|
||||
mLogBoxDialog = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCatalystInstanceDestroy() {
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mReactRootView != null) {
|
||||
mDevSupportManager.destroyRootView(mReactRootView);
|
||||
mReactRootView = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+6
@@ -8,6 +8,7 @@
|
||||
package com.facebook.react.devsupport;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.JavaJSExecutor;
|
||||
import com.facebook.react.bridge.JavaScriptExecutorFactory;
|
||||
@@ -32,4 +33,9 @@ public interface ReactInstanceManagerDevHelper {
|
||||
Activity getCurrentActivity();
|
||||
|
||||
JavaScriptExecutorFactory getJavaScriptExecutorFactory();
|
||||
|
||||
@Nullable
|
||||
View createRootView(String appKey);
|
||||
|
||||
void destroyRootView(View rootView);
|
||||
}
|
||||
|
||||
+6
@@ -7,6 +7,7 @@
|
||||
|
||||
package com.facebook.react.devsupport.interfaces;
|
||||
|
||||
import android.view.View;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
@@ -25,6 +26,11 @@ public interface DevSupportManager extends NativeModuleCallExceptionHandler {
|
||||
|
||||
void addCustomDevOption(String optionName, DevOptionHandler optionHandler);
|
||||
|
||||
@Nullable
|
||||
View createRootView(String appKey);
|
||||
|
||||
void destroyRootView(View rootView);
|
||||
|
||||
void showNewJSError(String message, ReadableArray details, int errorCookie);
|
||||
|
||||
void updateJSError(final String message, final ReadableArray details, final int errorCookie);
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@ import java.util.Map;
|
||||
public class ReactNativeVersion {
|
||||
public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
|
||||
"major", 0,
|
||||
"minor", 0,
|
||||
"minor", 62,
|
||||
"patch", 0,
|
||||
"prerelease", null);
|
||||
"prerelease", "rc.0");
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="catalyst_redbox_background">#eecc0000</color>
|
||||
<color name="catalyst_logbox_background">#ffffffff</color>
|
||||
</resources>
|
||||
|
||||
@@ -9,10 +9,22 @@
|
||||
<item name="android:outAnimation">@android:anim/fade_out</item>
|
||||
<item name="android:textColor">@android:color/white</item>
|
||||
</style>
|
||||
<style name="Theme.Catalyst.LogBox">
|
||||
<item name="android:windowTranslucentStatus">true</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:windowAnimationStyle">@style/Animation.Catalyst.LogBox</item>
|
||||
<item name="android:inAnimation">@android:anim/fade_in</item>
|
||||
<item name="android:outAnimation">@android:anim/fade_out</item>
|
||||
<item name="android:textColor">@android:color/white</item>
|
||||
</style>
|
||||
<style name="Animation.Catalyst.RedBox" parent="@android:style/Animation">
|
||||
<item name="android:windowEnterAnimation">@anim/catalyst_push_up_in</item>
|
||||
<item name="android:windowExitAnimation">@anim/catalyst_push_up_out</item>
|
||||
</style>
|
||||
<style name="Animation.Catalyst.LogBox" parent="@android:style/Animation">
|
||||
<item name="android:windowEnterAnimation">@anim/catalyst_push_up_in</item>
|
||||
<item name="android:windowExitAnimation">@anim/catalyst_push_up_out</item>
|
||||
</style>
|
||||
<style name="redboxButton">
|
||||
<item name="android:layout_width">0dp</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
|
||||
@@ -438,6 +438,9 @@ module.exports = {
|
||||
get unstable_RootTagContext(): RootTagContext {
|
||||
return require('./Libraries/ReactNative/RootTagContext');
|
||||
},
|
||||
get unstable_enableLogBox(): () => void {
|
||||
return require('./Libraries/YellowBox/YellowBox').__unstable_enableLogBox;
|
||||
},
|
||||
|
||||
// Prop Types
|
||||
get ColorPropType(): DeprecatedColorPropType {
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native",
|
||||
"version": "1000.0.0",
|
||||
"version": "0.62.0-rc.0",
|
||||
"bin": "./cli.js",
|
||||
"description": "A framework for building native apps using React",
|
||||
"license": "MIT",
|
||||
@@ -174,4 +174,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "16.11.0",
|
||||
"react-native": "1000.0.0"
|
||||
"react-native": "0.62.0-rc.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.6.2",
|
||||
|
||||
Reference in New Issue
Block a user