mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
LogBox - Optimistically symbolicate
Summary:
This diff adds optimistic loading for symbolicated stack traces by so that we (almost) never show a loading state for stack traces. Because of this, we also remove the "Stack Trace" status except when it is loading or failed. Also refactored the related components to hooks 🎣
Changelog: [Internal]
Reviewed By: mmmulani
Differential Revision: D18110403
fbshipit-source-id: a93b0a63e1c9490fea73ca6ec7c5707670bdea53
This commit is contained in:
committed by
Facebook Github Bot
parent
f91a21b2c0
commit
8524b6182d
@@ -82,6 +82,21 @@ export function add(level: LogLevel, args: $ReadOnlyArray<mixed>): void {
|
||||
handleUpdate();
|
||||
}
|
||||
|
||||
export function symbolicateLogNow(log: LogBoxLog) {
|
||||
log.symbolicate(() => {
|
||||
handleUpdate();
|
||||
});
|
||||
}
|
||||
export function retrySymbolicateLogNow(log: LogBoxLog) {
|
||||
log.retrySymbolicate(() => {
|
||||
handleUpdate();
|
||||
});
|
||||
}
|
||||
|
||||
export function symbolicateLogLazy(log: LogBoxLog) {
|
||||
log.symbolicate();
|
||||
}
|
||||
|
||||
export function clear(): void {
|
||||
if (logs.size > 0) {
|
||||
logs.clear();
|
||||
|
||||
@@ -63,12 +63,14 @@ class LogBoxLog {
|
||||
: this.stack;
|
||||
}
|
||||
|
||||
retrySymbolicate(callback: () => void): SymbolicationRequest {
|
||||
LogBoxSymbolication.deleteStack(this.stack);
|
||||
retrySymbolicate(callback?: () => void): SymbolicationRequest {
|
||||
if (this.symbolicated.status !== 'COMPLETE') {
|
||||
LogBoxSymbolication.deleteStack(this.stack);
|
||||
}
|
||||
return this.symbolicate(callback);
|
||||
}
|
||||
|
||||
symbolicate(callback: () => void): SymbolicationRequest {
|
||||
symbolicate(callback?: () => void): SymbolicationRequest {
|
||||
let aborted = false;
|
||||
|
||||
if (this.symbolicated.status !== 'COMPLETE') {
|
||||
@@ -81,7 +83,9 @@ class LogBoxLog {
|
||||
this.symbolicated = {error: null, stack: null, status: 'PENDING'};
|
||||
}
|
||||
if (!aborted) {
|
||||
callback();
|
||||
if (callback != null) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import * as React from 'react';
|
||||
import ScrollView from '../../Components/ScrollView/ScrollView';
|
||||
import StyleSheet from '../../StyleSheet/StyleSheet';
|
||||
import View from '../../Components/View/View';
|
||||
import * as LogBoxData from '../Data/LogBoxData';
|
||||
import LogBoxInspectorFooter from './LogBoxInspectorFooter';
|
||||
import LogBoxInspectorMessageHeader from './LogBoxInspectorMessageHeader';
|
||||
import LogBoxInspectorReactFrames from './LogBoxInspectorReactFrames';
|
||||
@@ -24,7 +25,6 @@ import LogBoxInspectorHeader from './LogBoxInspectorHeader';
|
||||
import * as LogBoxStyle from './LogBoxStyle';
|
||||
|
||||
import type LogBoxLog from '../Data/LogBoxLog';
|
||||
import type {SymbolicationRequest} from '../Data/LogBoxLog';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
onDismiss: () => void,
|
||||
@@ -34,75 +34,52 @@ type Props = $ReadOnly<{|
|
||||
selectedIndex: number,
|
||||
|}>;
|
||||
|
||||
class LogBoxInspector extends React.Component<Props> {
|
||||
_symbolication: ?SymbolicationRequest;
|
||||
function LogBoxInspector(props: Props): React.Node {
|
||||
const {logs, selectedIndex} = props;
|
||||
|
||||
_handleDismiss = () => {
|
||||
this.props.onDismiss();
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
const {logs, selectedIndex} = this.props;
|
||||
|
||||
const log = logs[selectedIndex];
|
||||
if (log == null) {
|
||||
return null;
|
||||
const log = logs[selectedIndex];
|
||||
React.useEffect(() => {
|
||||
// Symbolicate the visible log if it hasn't been already.
|
||||
if (log != null && log.symbolicated.status !== 'COMPLETE') {
|
||||
LogBoxData.symbolicateLogNow(log);
|
||||
}
|
||||
}, [log]);
|
||||
|
||||
return (
|
||||
<View style={styles.root}>
|
||||
<LogBoxInspectorHeader
|
||||
onSelectIndex={this._handleSelectIndex}
|
||||
selectedIndex={selectedIndex}
|
||||
total={logs.length}
|
||||
level={log.level}
|
||||
/>
|
||||
<LogBoxInspectorBody
|
||||
log={log}
|
||||
onRetry={this._handleRetrySymbolication}
|
||||
/>
|
||||
<LogBoxInspectorFooter
|
||||
onDismiss={this._handleDismiss}
|
||||
onMinimize={this.props.onMinimize}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this._handleSymbolication();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props): void {
|
||||
if (
|
||||
prevProps.logs[prevProps.selectedIndex] !==
|
||||
this.props.logs[this.props.selectedIndex]
|
||||
) {
|
||||
this._handleSymbolication();
|
||||
React.useEffect(() => {
|
||||
// Optimistically symbolicate the last and next logs.
|
||||
if (logs.length > 1) {
|
||||
const selected = selectedIndex;
|
||||
const lastIndex = logs.length - 1;
|
||||
const prevIndex = selected - 1 < 0 ? lastIndex : selected - 1;
|
||||
const nextIndex = selected + 1 > lastIndex ? 0 : selected + 1;
|
||||
LogBoxData.symbolicateLogLazy(logs[prevIndex]);
|
||||
LogBoxData.symbolicateLogLazy(logs[nextIndex]);
|
||||
}
|
||||
}, [logs, selectedIndex]);
|
||||
|
||||
function _handleRetry() {
|
||||
LogBoxData.retrySymbolicateLogNow(log);
|
||||
}
|
||||
|
||||
_handleRetrySymbolication = () => {
|
||||
this.forceUpdate(() => {
|
||||
const log = this.props.logs[this.props.selectedIndex];
|
||||
this._symbolication = log.retrySymbolicate(() => {
|
||||
this.forceUpdate();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
_handleSymbolication(): void {
|
||||
const log = this.props.logs[this.props.selectedIndex];
|
||||
if (log.symbolicated.status !== 'COMPLETE') {
|
||||
this._symbolication = log.symbolicate(() => {
|
||||
this.forceUpdate();
|
||||
});
|
||||
}
|
||||
if (log == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_handleSelectIndex = (selectedIndex: number): void => {
|
||||
this.props.onChangeSelectedIndex(selectedIndex);
|
||||
};
|
||||
return (
|
||||
<View style={styles.root}>
|
||||
<LogBoxInspectorHeader
|
||||
onSelectIndex={props.onChangeSelectedIndex}
|
||||
selectedIndex={selectedIndex}
|
||||
total={logs.length}
|
||||
level={log.level}
|
||||
/>
|
||||
<LogBoxInspectorBody log={log} onRetry={_handleRetry} />
|
||||
<LogBoxInspectorFooter
|
||||
onDismiss={props.onDismiss}
|
||||
onMinimize={props.onMinimize}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function LogBoxInspectorBody(props) {
|
||||
|
||||
@@ -19,8 +19,6 @@ import LogBoxImageSource from './LogBoxImageSource';
|
||||
import LogBoxButton from './LogBoxButton';
|
||||
import * as LogBoxStyle from './LogBoxStyle';
|
||||
|
||||
import type {CompositeAnimation} from '../../Animated/src/AnimatedImplementation';
|
||||
import type AnimatedInterpolation from '../../Animated/src/nodes/AnimatedInterpolation';
|
||||
import type {PressEvent} from '../../Types/CoreEventTypes';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
@@ -28,76 +26,15 @@ type Props = $ReadOnly<{|
|
||||
status: 'COMPLETE' | 'FAILED' | 'NONE' | 'PENDING',
|
||||
|}>;
|
||||
|
||||
type State = {|
|
||||
animation: ?CompositeAnimation,
|
||||
rotate: ?AnimatedInterpolation,
|
||||
|};
|
||||
|
||||
class LogBoxInspectorSourceMapStatus extends React.Component<Props, State> {
|
||||
state: State = {
|
||||
function LogBoxInspectorSourceMapStatus(props: Props): React.Node {
|
||||
const [state, setState] = React.useState({
|
||||
animation: null,
|
||||
rotate: null,
|
||||
};
|
||||
});
|
||||
|
||||
render(): React.Node {
|
||||
let image;
|
||||
let color;
|
||||
switch (this.props.status) {
|
||||
case 'COMPLETE':
|
||||
image = LogBoxImageSource.check;
|
||||
color = LogBoxStyle.getTextColor(0.4);
|
||||
break;
|
||||
case 'FAILED':
|
||||
image = LogBoxImageSource.alertTriangle;
|
||||
color = LogBoxStyle.getErrorColor(1);
|
||||
break;
|
||||
case 'PENDING':
|
||||
image = LogBoxImageSource.loader;
|
||||
color = LogBoxStyle.getWarningColor(1);
|
||||
break;
|
||||
}
|
||||
|
||||
return image == null ? null : (
|
||||
<LogBoxButton
|
||||
backgroundColor={{
|
||||
default: 'transparent',
|
||||
pressed: LogBoxStyle.getBackgroundColor(1),
|
||||
}}
|
||||
hitSlop={{bottom: 8, left: 8, right: 8, top: 8}}
|
||||
onPress={this.props.onPress}
|
||||
style={styles.root}>
|
||||
<Animated.Image
|
||||
source={{height: 16, uri: image, width: 16}}
|
||||
style={[
|
||||
styles.image,
|
||||
{tintColor: color},
|
||||
this.state.rotate == null
|
||||
? null
|
||||
: {transform: [{rotate: this.state.rotate}]},
|
||||
]}
|
||||
/>
|
||||
<Text style={[styles.text, {color}]}>Source Map</Text>
|
||||
</LogBoxButton>
|
||||
);
|
||||
}
|
||||
|
||||
componentDidMount(): void {
|
||||
this._updateAnimation();
|
||||
}
|
||||
|
||||
componentDidUpdate(): void {
|
||||
this._updateAnimation();
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (this.state.animation != null) {
|
||||
this.state.animation.stop();
|
||||
}
|
||||
}
|
||||
|
||||
_updateAnimation(): void {
|
||||
if (this.props.status === 'PENDING') {
|
||||
if (this.state.animation == null) {
|
||||
React.useEffect(() => {
|
||||
if (props.status === 'PENDING') {
|
||||
if (state.animation == null) {
|
||||
const animated = new Animated.Value(0);
|
||||
const animation = Animated.loop(
|
||||
Animated.timing(animated, {
|
||||
@@ -107,29 +44,69 @@ class LogBoxInspectorSourceMapStatus extends React.Component<Props, State> {
|
||||
useNativeDriver: true,
|
||||
}),
|
||||
);
|
||||
this.setState(
|
||||
{
|
||||
animation,
|
||||
rotate: animated.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ['0deg', '360deg'],
|
||||
}),
|
||||
},
|
||||
() => {
|
||||
animation.start();
|
||||
},
|
||||
);
|
||||
setState({
|
||||
animation,
|
||||
rotate: animated.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: ['0deg', '360deg'],
|
||||
}),
|
||||
});
|
||||
animation.start();
|
||||
}
|
||||
} else {
|
||||
if (this.state.animation != null) {
|
||||
this.state.animation.stop();
|
||||
this.setState({
|
||||
if (state.animation != null) {
|
||||
state.animation.stop();
|
||||
setState({
|
||||
animation: null,
|
||||
rotate: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (state.animation != null) {
|
||||
state.animation.stop();
|
||||
}
|
||||
};
|
||||
}, [props.status, state.animation]);
|
||||
|
||||
let image;
|
||||
let color;
|
||||
switch (props.status) {
|
||||
case 'FAILED':
|
||||
image = LogBoxImageSource.alertTriangle;
|
||||
color = LogBoxStyle.getErrorColor(1);
|
||||
break;
|
||||
case 'PENDING':
|
||||
image = LogBoxImageSource.loader;
|
||||
color = LogBoxStyle.getWarningColor(1);
|
||||
break;
|
||||
}
|
||||
|
||||
if (props.status === 'COMPLETE') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return image == null ? null : (
|
||||
<LogBoxButton
|
||||
backgroundColor={{
|
||||
default: 'transparent',
|
||||
pressed: LogBoxStyle.getBackgroundColor(1),
|
||||
}}
|
||||
hitSlop={{bottom: 8, left: 8, right: 8, top: 8}}
|
||||
onPress={props.onPress}
|
||||
style={styles.root}>
|
||||
<Animated.Image
|
||||
source={{height: 16, uri: image, width: 16}}
|
||||
style={[
|
||||
styles.image,
|
||||
{tintColor: color},
|
||||
state.rotate == null ? null : {transform: [{rotate: state.rotate}]},
|
||||
]}
|
||||
/>
|
||||
<Text style={[styles.text, {color}]}>Source Map</Text>
|
||||
</LogBoxButton>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
|
||||
@@ -79,7 +79,7 @@ function StackFrameHeader(props) {
|
||||
<View style={stackStyles.heading}>
|
||||
<Text style={stackStyles.headingText}>Stack</Text>
|
||||
<LogBoxInspectorSourceMapStatus
|
||||
onPress={props.status === 'FAILED' ? props.onRetry() : null}
|
||||
onPress={props.status !== 'COMPLETE' ? props.onRetry : null}
|
||||
status={props.status}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -20,63 +20,42 @@ import LogBoxButton from './LogBoxButton';
|
||||
import * as LogBoxStyle from './LogBoxStyle';
|
||||
import LogBoxLog from '../Data/LogBoxLog';
|
||||
import LogBoxMessage from './LogBoxMessage';
|
||||
import * as LogBoxData from '../Data/LogBoxData';
|
||||
|
||||
type Props = $ReadOnly<{|
|
||||
log: LogBoxLog,
|
||||
totalLogCount: number,
|
||||
level: 'warn' | 'error',
|
||||
onPressOpen: (index: number) => void,
|
||||
onPressOpen: () => void,
|
||||
onPressList: () => void,
|
||||
onPressDismiss: () => void,
|
||||
|}>;
|
||||
|
||||
class LogBoxLogNotification extends React.Component<Props> {
|
||||
static GUTTER: number = StyleSheet.hairlineWidth;
|
||||
static HEIGHT: number = 48;
|
||||
function LogBoxLogNotification(props: Props): React.Node {
|
||||
const {totalLogCount, level, log} = props;
|
||||
|
||||
shouldComponentUpdate(nextProps: Props): boolean {
|
||||
const prevProps = this.props;
|
||||
return (
|
||||
prevProps.onPressOpen !== nextProps.onPressOpen ||
|
||||
prevProps.onPressList !== nextProps.onPressList ||
|
||||
prevProps.onPressDismiss !== nextProps.onPressDismiss ||
|
||||
prevProps.log !== nextProps.log
|
||||
);
|
||||
}
|
||||
// Eagerly symbolicate so the stack is available when pressing to inspect.
|
||||
React.useEffect(() => {
|
||||
LogBoxData.symbolicateLogLazy(log);
|
||||
}, [log]);
|
||||
|
||||
_handlePressOpen = () => {
|
||||
this.props.onPressOpen(0);
|
||||
};
|
||||
|
||||
_handlePressList = () => {
|
||||
this.props.onPressList();
|
||||
};
|
||||
|
||||
_handlePressDismiss = () => {
|
||||
this.props.onPressDismiss();
|
||||
};
|
||||
|
||||
render(): React.Node {
|
||||
const {totalLogCount, level, log} = this.props;
|
||||
|
||||
return (
|
||||
<View style={toastStyles.container}>
|
||||
<LogBoxButton
|
||||
onPress={this._handlePressOpen}
|
||||
style={toastStyles.press}
|
||||
backgroundColor={{
|
||||
default: LogBoxStyle.getBackgroundColor(1),
|
||||
pressed: LogBoxStyle.getBackgroundColor(0.9),
|
||||
}}>
|
||||
<View style={toastStyles.content}>
|
||||
<CountBadge count={totalLogCount} level={level} />
|
||||
<Message message={log.message} />
|
||||
<DismissButton onPress={this._handlePressDismiss} />
|
||||
</View>
|
||||
</LogBoxButton>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View style={toastStyles.container}>
|
||||
<LogBoxButton
|
||||
onPress={props.onPressOpen}
|
||||
style={toastStyles.press}
|
||||
backgroundColor={{
|
||||
default: LogBoxStyle.getBackgroundColor(1),
|
||||
pressed: LogBoxStyle.getBackgroundColor(0.9),
|
||||
}}>
|
||||
<View style={toastStyles.content}>
|
||||
<CountBadge count={totalLogCount} level={level} />
|
||||
<Message message={log.message} />
|
||||
<DismissButton onPress={props.onPressDismiss} />
|
||||
</View>
|
||||
</LogBoxButton>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function CountBadge(props) {
|
||||
@@ -208,19 +187,19 @@ const dismissStyles = StyleSheet.create({
|
||||
|
||||
const toastStyles = StyleSheet.create({
|
||||
container: {
|
||||
height: LogBoxLogNotification.HEIGHT,
|
||||
height: 48,
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
marginTop: LogBoxLogNotification.GUTTER,
|
||||
marginTop: 0.5,
|
||||
backgroundColor: LogBoxStyle.getTextColor(1),
|
||||
},
|
||||
press: {
|
||||
height: LogBoxLogNotification.HEIGHT,
|
||||
height: 48,
|
||||
position: 'relative',
|
||||
width: '100%',
|
||||
justifyContent: 'center',
|
||||
marginTop: LogBoxLogNotification.GUTTER,
|
||||
marginTop: 0.5,
|
||||
paddingHorizontal: 12,
|
||||
},
|
||||
content: {
|
||||
|
||||
@@ -17,7 +17,23 @@ const LogBoxInspectorSourceMapStatus = require('../LogBoxInspectorSourceMapStatu
|
||||
const render = require('../../../../jest/renderer');
|
||||
|
||||
describe('LogBoxInspectorSourceMapStatus', () => {
|
||||
it('should render complete', () => {
|
||||
it('should render for failed', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxInspectorSourceMapStatus onPress={() => {}} status="FAILED" />,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render for pending', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxInspectorSourceMapStatus onPress={() => {}} status="PENDING" />,
|
||||
);
|
||||
|
||||
expect(output).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should render null for complete', () => {
|
||||
const output = render.shallowRender(
|
||||
<LogBoxInspectorSourceMapStatus onPress={() => {}} status="COMPLETE" />,
|
||||
);
|
||||
|
||||
+73
-4
@@ -1,6 +1,6 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`LogBoxInspectorSourceMapStatus should render complete 1`] = `
|
||||
exports[`LogBoxInspectorSourceMapStatus should render for failed 1`] = `
|
||||
<LogBoxButton
|
||||
backgroundColor={
|
||||
Object {
|
||||
@@ -31,7 +31,7 @@ exports[`LogBoxInspectorSourceMapStatus should render complete 1`] = `
|
||||
source={
|
||||
Object {
|
||||
"height": 16,
|
||||
"uri": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAf0lEQVRYhe2UvQ2AIBQGL3EBR3AESkv3bxxFN8DmWUgwvkI+En1X0cBd+IMg+DuDyDMCs413kfMiX4EMbD3l8oCaPIU85B4mYLEF5XJscrYFPRGvb/sZ4IlocubJGdH0wj1FSG77XYT0qdUi5O+8jOjyyZQRUnkZ0UUeBMF3OQC/0VsyGlxligAAAABJRU5ErkJggg==",
|
||||
"uri": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABVklEQVRYheWX4U3DMBBGH4gBMoJHyAgeoSNkAxjBG5QNOkJHCGzQDcoGZQP4gY3Oqe1cEscS4pNOqs9Jvqvv6ZrCf9fDhnutD4A3H810Br4mcW5l7hLmIdze5mZi+OJD5syeBYzC6CjyR5Ef9zI/CJMb0Im9zufC/qG2eQdchcGQuGYQ+9dJgZvl0B2xbJGrZW6IIevFXp9YVwcyB540syJfFcgSeJb0cVcDcg68XAFQCUhH+ShLBcBGIA158LQFqIB8zBRwEp9fgctcxQld/L2pZxZVAk/KkucjaDGQmoknrz35KEE2sABIRxm8tVIBaZgHb61UQOYmXk7aFgQVJ6QWPCnLAriYAVILnpTxD7yh/9EZiIEE4m+y29uMkGy1nQ6i9wYFRB5PwKdYP/v1msmnUe89gn695bG0iqjdXeMiRu9599csvGKZ0jlu0Ac/7d2rxX9Q37HW6QfX/ZguAAAAAElFTkSuQmCC",
|
||||
"width": 16,
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ exports[`LogBoxInspectorSourceMapStatus should render complete 1`] = `
|
||||
"tintColor": "rgba(255, 255, 255, 0.4)",
|
||||
},
|
||||
Object {
|
||||
"tintColor": "rgba(255, 255, 255, 0.4)",
|
||||
"tintColor": "rgba(243, 83, 105, 1)",
|
||||
},
|
||||
null,
|
||||
]
|
||||
@@ -57,7 +57,7 @@ exports[`LogBoxInspectorSourceMapStatus should render complete 1`] = `
|
||||
"lineHeight": 16,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(255, 255, 255, 0.4)",
|
||||
"color": "rgba(243, 83, 105, 1)",
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -66,3 +66,72 @@ exports[`LogBoxInspectorSourceMapStatus should render complete 1`] = `
|
||||
</Text>
|
||||
</LogBoxButton>
|
||||
`;
|
||||
|
||||
exports[`LogBoxInspectorSourceMapStatus should render for pending 1`] = `
|
||||
<LogBoxButton
|
||||
backgroundColor={
|
||||
Object {
|
||||
"default": "transparent",
|
||||
"pressed": "rgba(51, 51, 51, 1)",
|
||||
}
|
||||
}
|
||||
hitSlop={
|
||||
Object {
|
||||
"bottom": 8,
|
||||
"left": 8,
|
||||
"right": 8,
|
||||
"top": 8,
|
||||
}
|
||||
}
|
||||
onPress={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"borderRadius": 12,
|
||||
"flexDirection": "row",
|
||||
"height": 24,
|
||||
"paddingHorizontal": 8,
|
||||
}
|
||||
}
|
||||
>
|
||||
<AnimatedComponent
|
||||
source={
|
||||
Object {
|
||||
"height": 16,
|
||||
"uri": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABN0lEQVRYhe2WzU3EMBCFP34KyJEjJaQDXAIlJJ24BSow2wEdhHSwJSwd7JHbcmC0mOxMnDiWDIInWbHkN29exo4n8IvRAEFGU8OAA04yulyR60Jm7msbyIZloAMGwBfI4UWrWxM08LW/weC4iOMNTog4g0awKjBG827GxBwC3996NHizAifsSrTRmlsZm23CT9adktyXSq6ZUPdxgiXnZzW8CLcLuC3lvqA/gCt5NtjlPQL7TP0Wu1HtRRu4PO3T4TKTz2kG+AG9IN6CR/Su9iojBw69egfghWgL/pGCp+JFVPUqTjWjlsuqeAo1o6rt2C8QcNiV0UxoHPMieojmz0CfMKyhl1hN84xbI3gnz5Ftp7kH3iT5LsFdDUf6pzSJ6r2glIFDbuDNhqRH4I7Pvv4EvG/QqocP2Jh/xzzX/zUAAAAASUVORK5CYII=",
|
||||
"width": 16,
|
||||
}
|
||||
}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"marginEnd": 4,
|
||||
"tintColor": "rgba(255, 255, 255, 0.4)",
|
||||
},
|
||||
Object {
|
||||
"tintColor": "rgba(250, 186, 48, 1)",
|
||||
},
|
||||
null,
|
||||
]
|
||||
}
|
||||
/>
|
||||
<Text
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"fontSize": 12,
|
||||
"includeFontPadding": false,
|
||||
"lineHeight": 16,
|
||||
},
|
||||
Object {
|
||||
"color": "rgba(250, 186, 48, 1)",
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
Source Map
|
||||
</Text>
|
||||
</LogBoxButton>
|
||||
`;
|
||||
|
||||
exports[`LogBoxInspectorSourceMapStatus should render null for complete 1`] = `null`;
|
||||
|
||||
Reference in New Issue
Block a user