/** * 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 'react-emotion'; import React from 'react'; import {BackgroundColorProperty} from 'csstype'; import { TableBodyRow, TableColumnSizes, TableColumns, TableBodyColumn, } from './table/types'; const Padder = styled('div')( ({ padded, backgroundColor, }: { padded?: boolean; backgroundColor?: BackgroundColorProperty; }) => ({ padding: padded ? 10 : 0, backgroundColor, }), ); const Container = styled('div')( ({isCrash, padded}: {isCrash?: boolean; padded?: boolean}) => ({ 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}: {isCrash?: boolean}) => ({ color: isCrash ? colors.red : 'inherit', padding: 8, alignItems: 'center', minHeight: 32, })); const Reason = styled(Text)(({isCrash}: {isCrash?: boolean}) => ({ color: isCrash ? colors.red : colors.light80, fontWeight: 'bold', fontSize: 13, })); const Line = styled(Text)( ({isCrash, isBold}: {isCrash?: boolean; isBold?: boolean}) => ({ 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, }; type Child = { isBold?: boolean; library?: string | null | undefined; address?: string | null | undefined; caller?: string | null | undefined; lineNumber?: string | null | undefined; message?: string | null | undefined; }; /** * Display a stack trace */ export default class StackTrace extends Component<{ children: Child[]; /** * 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]) as Array).reduce< TableColumns >((acc, cv) => { if (cv !== 'isBold') { acc[cv] = { value: cv, }; } return acc; }, {}); const columnOrder = Object.keys(COLUMNS).map(key => ({ key, visible: Boolean(columns[key]), })); const columnSizes = (Object.keys(COLUMNS) as Array< keyof typeof COLUMNS >).reduce((acc, cv: keyof typeof COLUMNS) => { acc[cv] = COLUMNS[cv] === 'flex' ? 'flex' : children.reduce( (acc, line) => Math.max(acc, line[cv] ? line[cv]!.length : 0 || 0), 0, ) * 8 + 16; // approx 8px per character + 16px padding left/right return acc; }, {}); const rows: TableBodyRow[] = children.map((l, i) => ({ key: String(i), columns: (Object.keys(columns) as Array).reduce<{ [key: string]: TableBodyColumn; }>((acc, cv) => { acc[cv] = { align: cv === 'lineNumber' ? 'right' : 'left', value: ( {String(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> )} ); } }