Show framework event metadata documentation in detail view and tree select

Reviewed By: lblasa

Differential Revision: D48348090

fbshipit-source-id: e48547508b78178b278f72ce72fc579c9f015570
This commit is contained in:
Luke De Feo
2023-08-21 04:24:16 -07:00
committed by Facebook GitHub Bot
parent f5bc03c263
commit 4918d21df8
5 changed files with 82 additions and 13 deletions

View File

@@ -7,9 +7,11 @@
* @format * @format
*/ */
import {TreeSelect} from 'antd'; import React, {ReactNode} from 'react';
import {FrameworkEventType} from '../../ClientTypes'; import {InfoCircleOutlined} from '@ant-design/icons';
import React from 'react'; import {Tooltip, TreeSelect} from 'antd';
import {Layout, theme} from 'flipper-plugin';
import {FrameworkEventMetadata, FrameworkEventType} from '../../ClientTypes';
export function FrameworkEventsTreeSelect({ export function FrameworkEventsTreeSelect({
treeData, treeData,
@@ -55,7 +57,8 @@ export function FrameworkEventsTreeSelect({
} }
type TreeSelectNode = { type TreeSelectNode = {
title: string; title: ReactNode;
titleValue: string;
key: string; key: string;
value: string; value: string;
children: TreeSelectNode[]; children: TreeSelectNode[];
@@ -84,8 +87,11 @@ export const frameworkEventSeparator = '.';
/** /**
* transformed flat event type data structure into tree * transformed flat event type data structure into tree
*/ */
export function buildTreeSelectData(eventTypes: string[]): TreeSelectNode[] { export function buildTreeSelectData(
const root: TreeSelectNode = buildTreeSelectNode('root', 'root'); eventTypes: string[],
metadata: Map<FrameworkEventType, FrameworkEventMetadata>,
): TreeSelectNode[] {
const root: TreeSelectNode = buildTreeSelectNode('root', 'root', metadata);
eventTypes.forEach((eventType) => { eventTypes.forEach((eventType) => {
const eventSubtypes = eventType.split(frameworkEventSeparator); const eventSubtypes = eventType.split(frameworkEventSeparator);
@@ -95,7 +101,7 @@ export function buildTreeSelectData(eventTypes: string[]): TreeSelectNode[] {
for (let i = 0; i < eventSubtypes.length - 1; i++) { for (let i = 0; i < eventSubtypes.length - 1; i++) {
let foundChild = false; let foundChild = false;
for (const child of currentNode.children) { for (const child of currentNode.children) {
if (child.title === eventSubtypes[i]) { if (child.titleValue === eventSubtypes[i]) {
currentNode = child; currentNode = child;
foundChild = true; foundChild = true;
break; break;
@@ -105,6 +111,7 @@ export function buildTreeSelectData(eventTypes: string[]): TreeSelectNode[] {
const newNode: TreeSelectNode = buildTreeSelectNode( const newNode: TreeSelectNode = buildTreeSelectNode(
eventSubtypes[i], eventSubtypes[i],
eventSubtypes.slice(0, i + 1).join(frameworkEventSeparator), eventSubtypes.slice(0, i + 1).join(frameworkEventSeparator),
metadata,
); );
currentNode.children.push(newNode); currentNode.children.push(newNode);
@@ -118,6 +125,7 @@ export function buildTreeSelectData(eventTypes: string[]): TreeSelectNode[] {
eventSubtypes eventSubtypes
.slice(0, eventSubtypes.length) .slice(0, eventSubtypes.length)
.join(frameworkEventSeparator), .join(frameworkEventSeparator),
metadata,
), ),
); );
}); });
@@ -125,9 +133,29 @@ export function buildTreeSelectData(eventTypes: string[]): TreeSelectNode[] {
return root.children; return root.children;
} }
function buildTreeSelectNode(title: string, fullValue: string): TreeSelectNode { function buildTreeSelectNode(
title: string,
fullValue: string,
metadata: Map<FrameworkEventType, FrameworkEventMetadata>,
): TreeSelectNode {
const documentation = metadata.get(fullValue)?.documentation;
return { return {
title: title, title: (
<Layout.Horizontal gap="small" center>
{title}
{documentation && (
<Tooltip title={documentation}>
<InfoCircleOutlined
style={{
color: theme.primaryColor,
fontSize: theme.fontSize.large,
}}
/>
</Tooltip>
)}
</Layout.Horizontal>
),
titleValue: title,
key: fullValue, key: fullValue,
value: fullValue, value: fullValue,
children: [], children: [],

View File

@@ -43,6 +43,7 @@ export const Inspector: React.FC<Props> = ({
const instance = usePlugin(plugin); const instance = usePlugin(plugin);
const selectedNodeId = useValue(instance.uiState.selectedNode)?.id; const selectedNodeId = useValue(instance.uiState.selectedNode)?.id;
const frameworkEventMetadata = useValue(instance.frameworkEventMetadata);
const selectedNode = selectedNodeId ? nodes.get(selectedNodeId) : undefined; const selectedNode = selectedNodeId ? nodes.get(selectedNodeId) : undefined;
if (!selectedNode) { if (!selectedNode) {
return <NoData message="Please select a node to view its details" />; return <NoData message="Please select a node to view its details" />;
@@ -122,6 +123,7 @@ export const Inspector: React.FC<Props> = ({
</Tooltip> </Tooltip>
}> }>
<FrameworkEventsInspector <FrameworkEventsInspector
frameworkEventMetadata={frameworkEventMetadata}
node={selectedNode} node={selectedNode}
events={selectedFrameworkEvents} events={selectedFrameworkEvents}
showExtra={showExtra} showExtra={showExtra}

View File

@@ -14,7 +14,12 @@ import {
theme, theme,
TimelineDataDescription, TimelineDataDescription,
} from 'flipper-plugin'; } from 'flipper-plugin';
import {FrameworkEvent, ClientNode} from '../../../ClientTypes'; import {
FrameworkEvent,
ClientNode,
FrameworkEventType,
FrameworkEventMetadata,
} from '../../../ClientTypes';
import React, {ReactNode, useState} from 'react'; import React, {ReactNode, useState} from 'react';
import {StackTraceInspector} from './StackTraceInspector'; import {StackTraceInspector} from './StackTraceInspector';
import {Collapse, Descriptions, Select, Tag} from 'antd'; import {Collapse, Descriptions, Select, Tag} from 'antd';
@@ -29,11 +34,13 @@ type Props = {
node: ClientNode; node: ClientNode;
events: readonly FrameworkEvent[]; events: readonly FrameworkEvent[];
showExtra?: (title: string, element: ReactNode) => void; showExtra?: (title: string, element: ReactNode) => void;
frameworkEventMetadata: Map<FrameworkEventType, FrameworkEventMetadata>;
}; };
export const FrameworkEventsInspector: React.FC<Props> = ({ export const FrameworkEventsInspector: React.FC<Props> = ({
node, node,
events, events,
showExtra, showExtra,
frameworkEventMetadata,
}) => { }) => {
const allThreads = uniqBy(events, 'thread').map((event) => event.thread); const allThreads = uniqBy(events, 'thread').map((event) => event.thread);
const [filteredThreads, setFilteredThreads] = useState<Set<string>>( const [filteredThreads, setFilteredThreads] = useState<Set<string>>(
@@ -62,7 +69,10 @@ export const FrameworkEventsInspector: React.FC<Props> = ({
<Layout.Container gap="tiny"> <Layout.Container gap="tiny">
<FrameworkEventsTreeSelect <FrameworkEventsTreeSelect
placeholder="Select events types to filter by" placeholder="Select events types to filter by"
treeData={buildTreeSelectData(allEventTypes)} treeData={buildTreeSelectData(
allEventTypes,
frameworkEventMetadata,
)}
width={250} width={250}
onSetEventSelected={(eventType, selected) => { onSetEventSelected={(eventType, selected) => {
setFilteredEventTypes((cur) => setFilteredEventTypes((cur) =>
@@ -100,7 +110,11 @@ export const FrameworkEventsInspector: React.FC<Props> = ({
const event = filteredEvents[idx]; const event = filteredEvents[idx];
showExtra?.( showExtra?.(
'Event details', 'Event details',
<EventDetails event={event} node={node} />, <EventDetails
frameworkEventMetadata={frameworkEventMetadata.get(event.type)}
event={event}
node={node}
/>,
); );
}} }}
timeline={{ timeline={{
@@ -122,7 +136,9 @@ export const FrameworkEventsInspector: React.FC<Props> = ({
function EventDetails({ function EventDetails({
event, event,
node, node,
frameworkEventMetadata,
}: { }: {
frameworkEventMetadata?: FrameworkEventMetadata;
event: FrameworkEvent; event: FrameworkEvent;
node: ClientNode; node: ClientNode;
}) { }) {
@@ -138,6 +154,12 @@ function EventDetails({
<Layout.Container> <Layout.Container>
<Descriptions size="small" bordered column={1}> <Descriptions size="small" bordered column={1}>
<Descriptions.Item label="Event type">{event.type}</Descriptions.Item> <Descriptions.Item label="Event type">{event.type}</Descriptions.Item>
{frameworkEventMetadata && (
<Descriptions.Item label="Event documentation">
{frameworkEventMetadata?.documentation}
</Descriptions.Item>
)}
<Descriptions.Item label="Thread"> <Descriptions.Item label="Thread">
<Tag color={threadToColor(event.thread)}>{event.thread}</Tag> <Tag color={threadToColor(event.thread)}>{event.thread}</Tag>
</Descriptions.Item> </Descriptions.Item>

View File

@@ -17,7 +17,7 @@ import {
SearchOutlined, SearchOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import {usePlugin, useValue, Layout} from 'flipper-plugin'; import {usePlugin, useValue, Layout} from 'flipper-plugin';
import {FrameworkEventType} from '../../ClientTypes'; import {FrameworkEventMetadata, FrameworkEventType} from '../../ClientTypes';
import { import {
buildTreeSelectData, buildTreeSelectData,
FrameworkEventsTreeSelect, FrameworkEventsTreeSelect,
@@ -35,6 +35,8 @@ export const TreeControls: React.FC = () => {
instance.uiState.frameworkEventMonitoring, instance.uiState.frameworkEventMonitoring,
); );
const frameworkEventMetadata = useValue(instance.frameworkEventMetadata);
const [showFrameworkEventsModal, setShowFrameworkEventsModal] = const [showFrameworkEventsModal, setShowFrameworkEventsModal] =
useState(false); useState(false);
@@ -72,6 +74,7 @@ export const TreeControls: React.FC = () => {
</Tooltip> </Tooltip>
}></Button> }></Button>
<FrameworkEventsMonitoringModal <FrameworkEventsMonitoringModal
metadata={frameworkEventMetadata}
filterMainThreadMonitoring={filterMainThreadMonitoring} filterMainThreadMonitoring={filterMainThreadMonitoring}
onSetFilterMainThreadMonitoring={ onSetFilterMainThreadMonitoring={
instance.uiActions.onSetFilterMainThreadMonitoring instance.uiActions.onSetFilterMainThreadMonitoring
@@ -96,7 +99,9 @@ function FrameworkEventsMonitoringModal({
onSetFilterMainThreadMonitoring, onSetFilterMainThreadMonitoring,
filterMainThreadMonitoring, filterMainThreadMonitoring,
frameworkEventTypes, frameworkEventTypes,
metadata,
}: { }: {
metadata: Map<FrameworkEventType, FrameworkEventMetadata>;
visible: boolean; visible: boolean;
onCancel: () => void; onCancel: () => void;
onSetEventMonitored: ( onSetEventMonitored: (
@@ -113,6 +118,7 @@ function FrameworkEventsMonitoringModal({
const treeData = buildTreeSelectData( const treeData = buildTreeSelectData(
frameworkEventTypes.map(([type]) => type), frameworkEventTypes.map(([type]) => type),
metadata,
); );
return ( return (

View File

@@ -19,6 +19,7 @@ import {
PerformanceStatsEvent, PerformanceStatsEvent,
SnapshotInfo, SnapshotInfo,
ClientNode, ClientNode,
FrameworkEventMetadata,
} from './ClientTypes'; } from './ClientTypes';
import { import {
UIState, UIState,
@@ -50,6 +51,10 @@ export function plugin(client: PluginClient<Events>) {
limit: 10000, limit: 10000,
}); });
const frameworkEventMetadata = createState<
Map<FrameworkEventType, FrameworkEventMetadata>
>(new Map());
const uiState: UIState = createUIState(); const uiState: UIState = createUIState();
//this is the client data is what drives all of desktop UI //this is the client data is what drives all of desktop UI
@@ -78,6 +83,11 @@ export function plugin(client: PluginClient<Events>) {
draft.set(frameworkEventMeta.type, false); draft.set(frameworkEventMeta.type, false);
}); });
}); });
frameworkEventMetadata.update((draft) => {
event.frameworkEventMetadata?.forEach((frameworkEventMeta) => {
draft.set(frameworkEventMeta.type, frameworkEventMeta);
});
});
}); });
client.onConnect(() => { client.onConnect(() => {
@@ -301,6 +311,7 @@ export function plugin(client: PluginClient<Events>) {
uiActions: uiActions(uiState, nodesAtom, snapshot, mutableLiveClientData), uiActions: uiActions(uiState, nodesAtom, snapshot, mutableLiveClientData),
nodes: nodesAtom, nodes: nodesAtom,
frameworkEvents, frameworkEvents,
frameworkEventMetadata,
snapshot, snapshot,
metadata, metadata,
perfEvents, perfEvents,