diff --git a/Libraries/LogBox/Data/LogBoxData.js b/Libraries/LogBox/Data/LogBoxData.js index c33c027194b..cb3fd9cbd3f 100644 --- a/Libraries/LogBox/Data/LogBoxData.js +++ b/Libraries/LogBox/Data/LogBoxData.js @@ -10,6 +10,7 @@ ('use strict'); +import * as React from 'react'; import LogBoxLog from './LogBoxLog'; import {parseLogBoxException} from './parseLogBoxLog'; import type {LogLevel} from './LogBoxLog'; @@ -369,3 +370,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, + isDisabled: boolean, + selectedLogIndex: number, + |}>, +>; + +export function withSubscription( + WrappedComponent: SubscribedComponent, +): React.AbstractComponent<{||}> { + class LogBoxStateSubscription extends React.Component { + 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 or . + return null; + } + + return ( + + ); + } + + 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; +} diff --git a/Libraries/LogBox/LogBox.js b/Libraries/LogBox/LogBox.js index fb5ec6bc27e..b231b9396ad 100644 --- a/Libraries/LogBox/LogBox.js +++ b/Libraries/LogBox/LogBox.js @@ -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 { - static getDerivedStateFromError() { - return {hasError: true}; - } - - componentDidCatch(err, errorInfo) { - LogBoxData.reportLogBoxError(err, errorInfo.componentStack); - } - + LogBox = { // TODO: deprecated, replace with ignoreLogs - static ignoreWarnings(patterns: $ReadOnlyArray): void { + ignoreWarnings: (patterns: $ReadOnlyArray): void => { LogBox.ignoreLogs(patterns); - } + }, - static ignoreLogs(patterns: $ReadOnlyArray): void { + ignoreLogs: (patterns: $ReadOnlyArray): 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 or . - return null; - } - - return this.state.logs == null ? null : ( - - ); - } - - 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 { + LogBox = { // TODO: deprecated, replace with ignoreLogs - static ignoreWarnings(patterns: $ReadOnlyArray): void { + ignoreWarnings: (patterns: $ReadOnlyArray): void => { // Do nothing. - } + }, - static ignoreLogs(patterns: $ReadOnlyArray): void { + ignoreLogs: (patterns: $ReadOnlyArray): 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> & { +module.exports = (LogBox: { // TODO: deprecated, replace with ignoreLogs ignoreWarnings($ReadOnlyArray): void, ignoreLogs($ReadOnlyArray): void, diff --git a/Libraries/LogBox/LogBoxInspectorContainer.js b/Libraries/LogBox/LogBoxInspectorContainer.js new file mode 100644 index 00000000000..c8bfccff288 --- /dev/null +++ b/Libraries/LogBox/LogBoxInspectorContainer.js @@ -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, + 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 { + render(): React.Node { + return ( + = 0}> + + + + + ); + } + + _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<{||}>); diff --git a/Libraries/LogBox/UI/LogBoxContainer.js b/Libraries/LogBox/LogBoxNotificationContainer.js similarity index 50% rename from Libraries/LogBox/UI/LogBoxContainer.js rename to Libraries/LogBox/LogBoxNotificationContainer.js index d0070f7fa5a..bc5237560a4 100644 --- a/Libraries/LogBox/UI/LogBoxContainer.js +++ b/Libraries/LogBox/LogBoxNotificationContainer.js @@ -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, 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 ( - - - - ); - } - 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} /> )} @@ -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} /> )} @@ -126,4 +97,6 @@ const styles = StyleSheet.create({ }, }); -export default LogBoxContainer; +export default (LogBoxData.withSubscription( + _LogBoxNotificationContainer, +): React.AbstractComponent<{||}>); diff --git a/Libraries/LogBox/UI/LogBoxInspector.js b/Libraries/LogBox/UI/LogBoxInspector.js index 11b6150a38e..bf11270c3bb 100644 --- a/Libraries/LogBox/UI/LogBoxInspector.js +++ b/Libraries/LogBox/UI/LogBoxInspector.js @@ -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 ( - + - + ); } @@ -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, diff --git a/Libraries/LogBox/UI/LogBoxInspectorHeader.js b/Libraries/LogBox/UI/LogBoxInspectorHeader.js index 06d8147b388..a60baed457b 100644 --- a/Libraries/LogBox/UI/LogBoxInspectorHeader.js +++ b/Libraries/LogBox/UI/LogBoxInspectorHeader.js @@ -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, diff --git a/Libraries/LogBox/UI/LogBoxLogNotification.js b/Libraries/LogBox/UI/LogBoxNotification.js similarity index 100% rename from Libraries/LogBox/UI/LogBoxLogNotification.js rename to Libraries/LogBox/UI/LogBoxNotification.js diff --git a/Libraries/LogBox/UI/__tests__/LogBoxContainer-test.js b/Libraries/LogBox/UI/__tests__/LogBoxContainer-test.js deleted file mode 100644 index d9f3cc67d0c..00000000000 --- a/Libraries/LogBox/UI/__tests__/LogBoxContainer-test.js +++ /dev/null @@ -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( - {}} - 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( - {}} - 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( - {}} - 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( - {}} - 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( - {}} - 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( - {}} - 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( - {}} - 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(); - }); -}); diff --git a/Libraries/LogBox/UI/__tests__/LogBoxLogNotification-test.js b/Libraries/LogBox/UI/__tests__/LogBoxNotification-test.js similarity index 86% rename from Libraries/LogBox/UI/__tests__/LogBoxLogNotification-test.js rename to Libraries/LogBox/UI/__tests__/LogBoxNotification-test.js index 1eddb287047..1f056852f3b 100644 --- a/Libraries/LogBox/UI/__tests__/LogBoxLogNotification-test.js +++ b/Libraries/LogBox/UI/__tests__/LogBoxNotification-test.js @@ -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( - - + `; exports[`LogBoxContainer should render null with no logs 1`] = `null`; exports[`LogBoxContainer should render warning with selectedIndex 0 1`] = ` - - + `; diff --git a/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorHeader-test.js.snap b/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorHeader-test.js.snap index 1fb99437170..da2344e399e 100644 --- a/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorHeader-test.js.snap +++ b/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorHeader-test.js.snap @@ -39,7 +39,7 @@ exports[`LogBoxInspectorHeader should render both buttons for two total 1`] = ` { + it('should render null with no logs', () => { + const output = render.shallowRender( + , + ); + + expect(output).toMatchSnapshot(); + }); + + it('should render null with no selected log and disabled', () => { + const output = render.shallowRender( + , + ); + + expect(output).toMatchSnapshot(); + }); + + it('should render the latest warning notification', () => { + const output = render.shallowRender( + , + ); + + expect(output).toMatchSnapshot(); + }); + + it('should render the latest error notification', () => { + const output = render.shallowRender( + , + ); + + expect(output).toMatchSnapshot(); + }); + + it('should render both an error and warning notification', () => { + const output = render.shallowRender( + , + ); + + expect(output).toMatchSnapshot(); + }); + + it('should render selected fatal error even when disabled', () => { + const output = render.shallowRender( + , + ); + + expect(output).toMatchSnapshot(); + }); + + it('should render selected syntax error even when disabled', () => { + const output = render.shallowRender( + 199 | export default CrashReactApp; + | ^ + 200 |`, + }, + }), + ]} + />, + ); + + expect(output).toMatchSnapshot(); + }); +}); diff --git a/Libraries/LogBox/__tests__/LogBoxNotificationContainer-test.js b/Libraries/LogBox/__tests__/LogBoxNotificationContainer-test.js new file mode 100644 index 00000000000..5494ae1146b --- /dev/null +++ b/Libraries/LogBox/__tests__/LogBoxNotificationContainer-test.js @@ -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( + , + ); + + expect(output).toMatchSnapshot(); + }); +}); diff --git a/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxContainer-test.js.snap b/Libraries/LogBox/__tests__/__snapshots__/LogBoxInspectorContainer-test.js.snap similarity index 59% rename from Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxContainer-test.js.snap rename to Libraries/LogBox/__tests__/__snapshots__/LogBoxInspectorContainer-test.js.snap index 037c49d67ca..7182e10482b 100644 --- a/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxContainer-test.js.snap +++ b/Libraries/LogBox/__tests__/__snapshots__/LogBoxInspectorContainer-test.js.snap @@ -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`] = ` `; -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`] = ` - - - -`; +exports[`LogBoxNotificationContainer should render selected fatal error even when disabled 1`] = `null`; -exports[`LogBoxContainer should render selected syntax error even when disabled 1`] = ` - - 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} - /> - -`; +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`] = ` `; -exports[`LogBoxContainer should render the latest warning notification 1`] = ` +exports[`LogBoxNotificationContainer should render the latest warning notification 1`] = ` + + + + +`; diff --git a/Libraries/Modal/Modal.js b/Libraries/Modal/Modal.js index f803295228e..8cc55ffe572 100644 --- a/Libraries/Modal/Modal.js +++ b/Libraries/Modal/Modal.js @@ -229,7 +229,7 @@ class Modal extends React.Component { } const innerChildren = __DEV__ ? ( - + {this.props.children} ) : ( diff --git a/Libraries/ReactNative/AppContainer.js b/Libraries/ReactNative/AppContainer.js index 5966f19b921..30b3b212255 100644 --- a/Libraries/ReactNative/AppContainer.js +++ b/Libraries/ReactNative/AppContainer.js @@ -98,8 +98,9 @@ class AppContainer extends React.Component { if (__DEV__ && !this.props.internal_excludeLogBox) { if (!global.__RCTProfileIsProfiling) { if (global.__reactExperimentalLogBox) { - const LogBox = require('../LogBox/LogBox'); - logBox = ; + const LogBoxNotificationContainer = require('../LogBox/LogBoxNotificationContainer') + .default; + logBox = ; } else { const YellowBox = require('../YellowBox/YellowBox'); logBox = ; @@ -155,15 +156,6 @@ if (__DEV__) { 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(); diff --git a/Libraries/ReactNative/AppRegistry.js b/Libraries/ReactNative/AppRegistry.js index 81bef371de4..682db4682a0 100644 --- a/Libraries/ReactNative/AppRegistry.js +++ b/Libraries/ReactNative/AppRegistry.js @@ -294,7 +294,8 @@ const AppRegistry = { BatchedBridge.registerCallableModule('AppRegistry', AppRegistry); if (__DEV__) { - AppRegistry.registerComponent('LogBox', () => () => null); + const LogBoxInspector = require('../LogBox/LogBoxInspectorContainer').default; + AppRegistry.registerComponent('LogBox', () => LogBoxInspector); } module.exports = AppRegistry;