Improve framework event filters
Reviewed By: lblasa Differential Revision: D48393422 fbshipit-source-id: 18d92b53bd56c100b6d4bb6adc07ede0b4a46732
This commit is contained in:
committed by
Facebook GitHub Bot
parent
756a289883
commit
1bffe8bc6b
@@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import {Checkbox, Typography} from 'antd';
|
||||||
|
import {Layout, styled, theme} from 'flipper-plugin';
|
||||||
|
|
||||||
|
export function MultiSelectableDropDownItem<T>({
|
||||||
|
value,
|
||||||
|
selectedValues,
|
||||||
|
onSelect,
|
||||||
|
text,
|
||||||
|
}: {
|
||||||
|
value: T;
|
||||||
|
selectedValues: Set<T>;
|
||||||
|
onSelect: (value: T, selected: boolean) => void;
|
||||||
|
text: string;
|
||||||
|
}) {
|
||||||
|
const isSelected = selectedValues.has(value);
|
||||||
|
return (
|
||||||
|
<StyledMultiSelectDropDownItem
|
||||||
|
center
|
||||||
|
padv="small"
|
||||||
|
gap="small"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
onSelect(value, !isSelected);
|
||||||
|
}}>
|
||||||
|
<Checkbox
|
||||||
|
checked={isSelected}
|
||||||
|
onChange={() => {
|
||||||
|
onSelect(value, !isSelected);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Typography.Text>{text}</Typography.Text>
|
||||||
|
</StyledMultiSelectDropDownItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StyledMultiSelectDropDownItem = styled(Layout.Horizontal)({
|
||||||
|
':hover': {
|
||||||
|
backgroundColor: theme.backgroundWash,
|
||||||
|
},
|
||||||
|
height: 32,
|
||||||
|
});
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the MIT license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree.
|
||||||
|
*
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Menu} from 'antd';
|
||||||
|
import {theme} from 'flipper-plugin';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export function SelectableDropDownItem<T>({
|
||||||
|
value,
|
||||||
|
selectedValue,
|
||||||
|
onSelect,
|
||||||
|
text,
|
||||||
|
}: {
|
||||||
|
value: T;
|
||||||
|
selectedValue: T;
|
||||||
|
onSelect: (value: T) => void;
|
||||||
|
text: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Menu.Item
|
||||||
|
style={{
|
||||||
|
color:
|
||||||
|
value === selectedValue ? theme.primaryColor : theme.textColorActive,
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
onSelect(value);
|
||||||
|
}}>
|
||||||
|
{text}
|
||||||
|
</Menu.Item>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -22,15 +22,20 @@ import {
|
|||||||
} from '../../../ClientTypes';
|
} from '../../../ClientTypes';
|
||||||
import React, {ReactNode, useState} from 'react';
|
import React, {ReactNode, useState} from 'react';
|
||||||
import {StackTraceInspector} from './StackTraceInspector';
|
import {StackTraceInspector} from './StackTraceInspector';
|
||||||
import {Button, Collapse, Descriptions, Select, Tag} from 'antd';
|
|
||||||
import {frameworkEventSeparator} from '../../shared/FrameworkEventsTreeSelect';
|
|
||||||
import {
|
import {
|
||||||
buildTreeSelectData,
|
Badge,
|
||||||
FrameworkEventsTreeSelect,
|
Button,
|
||||||
} from '../../shared/FrameworkEventsTreeSelect';
|
Descriptions,
|
||||||
import {uniqBy} from 'lodash';
|
Dropdown,
|
||||||
import {TableOutlined} from '@ant-design/icons';
|
Tag,
|
||||||
|
Tooltip,
|
||||||
|
Typography,
|
||||||
|
} from 'antd';
|
||||||
|
import {frameworkEventSeparator} from '../../shared/FrameworkEventsTreeSelect';
|
||||||
|
import {last, startCase, uniqBy} from 'lodash';
|
||||||
|
import {FilterOutlined, TableOutlined} from '@ant-design/icons';
|
||||||
import {ViewMode} from '../../../DesktopTypes';
|
import {ViewMode} from '../../../DesktopTypes';
|
||||||
|
import {MultiSelectableDropDownItem} from '../../shared/MultiSelectableDropDownItem';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
node: ClientNode;
|
node: ClientNode;
|
||||||
@@ -39,6 +44,7 @@ type Props = {
|
|||||||
frameworkEventMetadata: Map<FrameworkEventType, FrameworkEventMetadata>;
|
frameworkEventMetadata: Map<FrameworkEventType, FrameworkEventMetadata>;
|
||||||
onSetViewMode: (viewMode: ViewMode) => void;
|
onSetViewMode: (viewMode: ViewMode) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FrameworkEventsInspector: React.FC<Props> = ({
|
export const FrameworkEventsInspector: React.FC<Props> = ({
|
||||||
node,
|
node,
|
||||||
events,
|
events,
|
||||||
@@ -68,28 +74,66 @@ export const FrameworkEventsInspector: React.FC<Props> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Container gap="small" padv="small">
|
<Layout.Container gap="small" padv="small">
|
||||||
|
<Layout.Right center gap>
|
||||||
|
<Typography.Title level={3}>Event timeline</Typography.Title>
|
||||||
|
|
||||||
|
<Layout.Horizontal center gap padh="medium">
|
||||||
{node.tags.includes('TreeRoot') && (
|
{node.tags.includes('TreeRoot') && (
|
||||||
|
<Tooltip title="Explore all tree events in table">
|
||||||
<Button
|
<Button
|
||||||
type="ghost"
|
shape="circle"
|
||||||
icon={<TableOutlined />}
|
icon={<TableOutlined />}
|
||||||
size="middle"
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
onSetViewMode({mode: 'frameworkEventsTable', treeRootId: node.id})
|
onSetViewMode({
|
||||||
}>
|
mode: 'frameworkEventsTable',
|
||||||
Explore all events
|
treeRootId: node.id,
|
||||||
</Button>
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<Collapse>
|
<Dropdown
|
||||||
<Collapse.Panel header="Filter events" key="1">
|
overlayStyle={{minWidth: 200}}
|
||||||
<Layout.Container gap="tiny">
|
overlay={
|
||||||
<FrameworkEventsTreeSelect
|
<Layout.Container
|
||||||
placeholder="Select events types to filter by"
|
gap="small"
|
||||||
treeData={buildTreeSelectData(
|
pad="small"
|
||||||
allEventTypes,
|
style={{
|
||||||
frameworkEventMetadata,
|
backgroundColor: theme.white,
|
||||||
|
borderRadius: theme.borderRadius,
|
||||||
|
boxShadow: `0 0 4px 1px rgba(0,0,0,0.10)`,
|
||||||
|
}}>
|
||||||
|
{allThreads.length > 1 && (
|
||||||
|
<>
|
||||||
|
<Typography.Text strong>By thread</Typography.Text>
|
||||||
|
{allThreads.map((thread) => (
|
||||||
|
<MultiSelectableDropDownItem
|
||||||
|
onSelect={(thread, selected) => {
|
||||||
|
setFilteredThreads((cur) =>
|
||||||
|
produce(cur, (draft) => {
|
||||||
|
if (selected) {
|
||||||
|
draft.add(thread);
|
||||||
|
} else {
|
||||||
|
draft.delete(thread);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
selectedValues={filteredThreads}
|
||||||
|
key={thread}
|
||||||
|
value={thread as string}
|
||||||
|
text={startCase(thread) as string}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
width={250}
|
|
||||||
onSetEventSelected={(eventType, selected) => {
|
{allEventTypes.length > 1 && (
|
||||||
|
<>
|
||||||
|
<Typography.Text strong>By event type</Typography.Text>
|
||||||
|
{allEventTypes.map((eventType) => (
|
||||||
|
<MultiSelectableDropDownItem
|
||||||
|
onSelect={(eventType, selected) => {
|
||||||
setFilteredEventTypes((cur) =>
|
setFilteredEventTypes((cur) =>
|
||||||
produce(cur, (draft) => {
|
produce(cur, (draft) => {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
@@ -100,22 +144,30 @@ export const FrameworkEventsInspector: React.FC<Props> = ({
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
selected={[...filteredEventTypes]}
|
selectedValues={filteredEventTypes}
|
||||||
/>
|
key={eventType}
|
||||||
<Select
|
value={eventType as string}
|
||||||
mode="multiple"
|
text={last(eventType.split('.')) as string}
|
||||||
style={{width: '250px'}}
|
|
||||||
placeholder="Select threads to filter by"
|
|
||||||
defaultValue={[] as string[]}
|
|
||||||
options={allThreads.map((thread) => ({
|
|
||||||
value: thread,
|
|
||||||
label: thread,
|
|
||||||
}))}
|
|
||||||
onChange={(value) => setFilteredThreads(new Set(value))}
|
|
||||||
/>
|
/>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Layout.Container>
|
</Layout.Container>
|
||||||
</Collapse.Panel>
|
}>
|
||||||
</Collapse>
|
<Button
|
||||||
|
shape="circle"
|
||||||
|
icon={
|
||||||
|
<Badge
|
||||||
|
offset={[8, -8]}
|
||||||
|
size="small"
|
||||||
|
count={filteredEventTypes.size + filteredThreads.size}>
|
||||||
|
<FilterOutlined style={{}} />
|
||||||
|
</Badge>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Dropdown>
|
||||||
|
</Layout.Horizontal>
|
||||||
|
</Layout.Right>
|
||||||
|
|
||||||
<TimelineDataDescription
|
<TimelineDataDescription
|
||||||
key={node.id}
|
key={node.id}
|
||||||
@@ -228,7 +280,7 @@ function formatDuration(nanoseconds: number): string {
|
|||||||
} else if (nanoseconds < 1_000_000_000_000) {
|
} else if (nanoseconds < 1_000_000_000_000) {
|
||||||
return `${(nanoseconds / 1_000_000_000).toFixed(2)} seconds`;
|
return `${(nanoseconds / 1_000_000_000).toFixed(2)} seconds`;
|
||||||
} else {
|
} else {
|
||||||
return `${(nanoseconds / 1_000_000_000_000).toFixed(2)} minutes`;
|
return `${(nanoseconds / (1_000_000_000 * 60)).toFixed(2)} minutes`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function eventTypeToName(eventType: string) {
|
function eventTypeToName(eventType: string) {
|
||||||
|
|||||||
@@ -9,7 +9,16 @@
|
|||||||
|
|
||||||
import React, {useState} from 'react';
|
import React, {useState} from 'react';
|
||||||
import {plugin} from '../../index';
|
import {plugin} from '../../index';
|
||||||
import {Button, Input, Modal, Tooltip, Typography, Space, Switch} from 'antd';
|
import {
|
||||||
|
Button,
|
||||||
|
Input,
|
||||||
|
Modal,
|
||||||
|
Tooltip,
|
||||||
|
Typography,
|
||||||
|
Space,
|
||||||
|
Switch,
|
||||||
|
Badge,
|
||||||
|
} from 'antd';
|
||||||
import {
|
import {
|
||||||
EyeOutlined,
|
EyeOutlined,
|
||||||
PauseCircleOutlined,
|
PauseCircleOutlined,
|
||||||
@@ -62,6 +71,13 @@ export const TreeControls: React.FC = () => {
|
|||||||
}></Button>
|
}></Button>
|
||||||
{frameworkEventMonitoring.size > 0 && (
|
{frameworkEventMonitoring.size > 0 && (
|
||||||
<>
|
<>
|
||||||
|
<Badge
|
||||||
|
size="small"
|
||||||
|
count={
|
||||||
|
[...frameworkEventMonitoring.values()].filter(
|
||||||
|
(val) => val === true,
|
||||||
|
).length
|
||||||
|
}>
|
||||||
<Button
|
<Button
|
||||||
type="default"
|
type="default"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
@@ -73,6 +89,7 @@ export const TreeControls: React.FC = () => {
|
|||||||
<EyeOutlined />
|
<EyeOutlined />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
}></Button>
|
}></Button>
|
||||||
|
</Badge>
|
||||||
<FrameworkEventsMonitoringModal
|
<FrameworkEventsMonitoringModal
|
||||||
metadata={frameworkEventMetadata}
|
metadata={frameworkEventMetadata}
|
||||||
filterMainThreadMonitoring={filterMainThreadMonitoring}
|
filterMainThreadMonitoring={filterMainThreadMonitoring}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
import {tracker} from '../../utils/tracker';
|
import {tracker} from '../../utils/tracker';
|
||||||
import {debounce} from 'lodash';
|
import {debounce} from 'lodash';
|
||||||
import {WireFrameMode} from '../../DesktopTypes';
|
import {WireFrameMode} from '../../DesktopTypes';
|
||||||
|
import {SelectableDropDownItem} from '../shared/SelectableDropDownItem';
|
||||||
export type TargetModeState =
|
export type TargetModeState =
|
||||||
| {
|
| {
|
||||||
state: 'selected';
|
state: 'selected';
|
||||||
@@ -182,31 +183,6 @@ export function VisualiserControls({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SelectableDropDownItem<T>({
|
|
||||||
value,
|
|
||||||
selectedValue,
|
|
||||||
onSelect,
|
|
||||||
text,
|
|
||||||
}: {
|
|
||||||
value: T;
|
|
||||||
selectedValue: T;
|
|
||||||
onSelect: (value: T) => void;
|
|
||||||
text: string;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<Menu.Item
|
|
||||||
style={{
|
|
||||||
color:
|
|
||||||
value === selectedValue ? theme.primaryColor : theme.textColorActive,
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
onSelect(value);
|
|
||||||
}}>
|
|
||||||
{text}
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const debouncedReportTargetAdjusted = debounce(() => {
|
const debouncedReportTargetAdjusted = debounce(() => {
|
||||||
tracker.track('target-mode-adjusted', {});
|
tracker.track('target-mode-adjusted', {});
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|||||||
Reference in New Issue
Block a user