diff --git a/src/ui/components/StackTrace.js b/src/ui/components/StackTrace.js new file mode 100644 index 000000000..3184dbe1b --- /dev/null +++ b/src/ui/components/StackTrace.js @@ -0,0 +1,170 @@ +/** + * Copyright 2018-present Facebook. + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * @format + */ + +import {Component} from 'react'; +import Text from './Text'; +import {colors} from './colors'; +import ManagedTable from './table/ManagedTable'; +import FlexRow from './FlexRow'; +import Glyph from './Glyph'; +import styled from '../styled'; + +const Padder = styled('div')(({padded, backgroundColor}) => ({ + padding: padded ? 10 : 0, + backgroundColor, +})); + +const Container = styled('div')(({isCrash, padded}) => ({ + backgroundColor: isCrash ? colors.redTint : 'transprent', + border: padded + ? `1px solid ${isCrash ? colors.red : colors.light15}` + : 'none', + borderRadius: padded ? 5 : 0, + overflow: 'hidden', +})); + +const Title = styled(FlexRow)(({isCrash}) => ({ + color: isCrash ? colors.red : 'inherit', + padding: 8, + alignItems: 'center', + minHeight: 32, +})); + +const Reason = styled(Text)(({isCrash}) => ({ + color: isCrash ? colors.red : colors.light80, + fontWeight: 'bold', + fontSize: 13, +})); + +const Line = styled(Text)(({isCrash, isBold}) => ({ + color: isCrash ? colors.red : colors.light80, + fontWeight: isBold ? 'bold' : 'normal', +})); + +const Icon = styled(Glyph)({marginRight: 5}); + +const COLUMNS = { + lineNumber: 40, + address: 150, + library: 150, + message: 'flex', + caller: 200, +}; + +/** + * Display a stack trace + */ +export default class StackTrace extends Component<{ + children: Array<{ + isBold?: boolean, + library?: ?string, + address?: ?string, + caller?: ?string, + lineNumber?: ?string, + message?: ?string, + }>, + /** + * Reason for the crash, displayed above the trace + */ + reason?: string, + /** + * Does the trace show a crash + */ + isCrash?: boolean, + /** + * Display the stack trace in a padded container + */ + padded?: boolean, + /** + * Background color of the stack trace + */ + backgroundColor?: string, +}> { + render() { + const {children} = this.props; + if (!children || children.length === 0) { + return null; + } + + const columns = Object.keys(children[0]).reduce((acc, cv) => { + if (cv !== 'isBold') { + acc[cv] = { + label: cv, + }; + } + return acc; + }, {}); + + const columnOrder = Object.keys(COLUMNS).map(key => ({ + key, + visible: Boolean(columns[key]), + })); + + const columnSizes = Object.keys(COLUMNS).reduce((acc, cv) => { + acc[cv] = + COLUMNS[cv] === 'flex' + ? 'flex' + : children.reduce( + (acc, line) => Math.max(acc, line[cv]?.length || 0), + 0, + ) * + 8 + + 16; // approx 8px per character + 16px padding left/right + + return acc; + }, {}); + + const rows = children.map((l, i) => ({ + key: i, + columns: Object.keys(columns).reduce((acc, cv) => { + acc[cv] = { + align: cv === 'lineNumber' ? 'right' : 'left', + value: ( + + {l[cv]} + + ), + }; + return acc; + }, {}), + })); + + return ( + + + {this.props.reason && ( + + {this.props.isCrash && ( + <Icon + name="stop" + variant="filled" + size={16} + color={colors.red} + /> + )} + <Reason isCrash={this.props.isCrash} code> + {this.props.reason} + </Reason> + + )} + + + + ); + } +} diff --git a/src/ui/index.js b/src/ui/index.js index f5443d32d..f3e06ea0c 100644 --- a/src/ui/index.js +++ b/src/ui/index.js @@ -133,6 +133,8 @@ export type {Filter} from './components/filter/types.js'; export {default as MarkerTimeline} from './components/MarkerTimeline.js'; +export {default as StackTrace} from './components/StackTrace.js'; + // export { SearchBox,